summaryrefslogtreecommitdiff
path: root/gtk
diff options
context:
space:
mode:
authorHavoc Pennington <hp@redhat.com>2000-06-02 03:14:07 +0000
committerOwen Taylor <otaylor@src.gnome.org>2000-06-02 03:14:07 +0000
commit9ae724bf4d72d1eed98f3e55a640b31ff552e8f1 (patch)
tree184c2860125ced111f7efcae065219ecec021308 /gtk
parent3f0092c4e63f2cc633e270e590a6c30a983f1c7f (diff)
downloadgdk-pixbuf-9ae724bf4d72d1eed98f3e55a640b31ff552e8f1.tar.gz
Remove all references to offscreen flag which was no longer used.
Thu Jun 1 23:05:13 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtkwidget.c: Remove all references to offscreen flag which was no longer used. * gtk/gtkprivate.h (enum): Remove unused flags and compress. * gtk/gtkframe.c (gtk_frame_set_label_widget): Check for non-null label_widget->parent. * gtk/gtkentry.c: Get rid of code to deal with PangoAttribute which no longer was used. * gdk/gdkpango.c (gdk_pango_context_get_info): make static. * gdk/gdkpango.c (gdk_draw_layout[_line]): Add checks for null arguments. * gdk/x11/gdkgeometry-x11.c (gdk_window_scroll): add check for destroyed windows. Thu Jun 1 13:48:45 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtkimmulticontext.c: Add a finalize method and unref the slave context there. * gtk/gtkinvisible.[ch]: Make reference counting behavior identical to GtkWindow. Thu Jun 1 01:54:11 2000 Owen Taylor <otaylor@redhat.com> * Makefile.am gdk/gdkpango.c: Copy the layout render function from pangox to here, so we can write them independent of rendering system, using GDK primitives. * gdk/gdkdrawable.h gdk/gdkdraw.c gdk/gdkwindow.c gdk/x11/gdkdrawable-x11.c: Remove draw_layout() from the vtable, since we have a rendering-system independent implementation in terms of draw_glyphs(). * gdk/gdkpango.c gdkdrawable.h (gdk_draw_layout_line): New function to render a single line. * gdk/x11/gdkpango.c: Move the guts of this file mostly into ../gdkpango.c, which simplifies things, since we don't have to deal with raw X gc's. Fri May 19 04:28:16 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtktextlayout.[ch]: Add get_log_attrs() function to get the logical attributes for a given GtkTextLine. Tue May 30 16:05:39 2000 Owen Taylor <otaylor@redhat.com> * gdk/x11/gdkfont-x11.c (gdk_font_charset_for_locale): Track g_locale_get_codeset() to g_get_codeset() change. Tue May 30 15:03:19 2000 Owen Taylor <otaylor@redhat.com> * gtk/testcalendar.c (calendar_font_selection_ok): Use font descriptions. * gtk/gtkentry.c (gtk_entry_draw_text): Center text within the entry. * gtk/gtkfontsel.c (gtk_font_selection_dialog_init): Start of redoing (vastly simplifying) for Pango. Still needs quite a bit of work. (Size selection is currently poor. List of predefined sizes is not a good idea, since all of these sizes won't necessarily be distinct.) Tue May 30 13:50:19 2000 Owen Taylor <otaylor@redhat.com> * gdk/x11/gdkfont-x11.c (gdk_font_charset_for_locale): Handle CODESET results for LANG=C. Mon May 29 15:49:10 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtkrc.[ch]: Add a 'font_name' declaration to RC which takes a stringized pango font description; ignore the older 'font' and 'fontset' declarations. * gtk/gtkstyle.c gtk/gtkrc.c: Fill in the style->font field with a GdkFont derived via gdk_font_from_description(), for compatibility. (Should we just remove it entirely? Probably too much compatibility breakage, but people should be migrating to the new Pango stuff as quickly as possible.) Mon May 29 15:47:41 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtkentry.c gtk/gtkclist.c: s/pango_font_unref/g_object_unref/. Mon May 29 15:44:46 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtkcalender.c: Roughly pango-ized. Really needs redoing; there are some bugs in size allocation right now, the semi-existant distinction between header / day fonts was removed, but, with Pango, could actually be made functional in a nice way. * gtk/testcalender: Move calender from examples into this directory as a test program. (We really need to restrcture testgtk into a whole directory full of tests for every widget or functionality group, separated into multiple .c files.) Mon May 29 15:19:56 2000 Owen Taylor <otaylor@redhat.com> * gtk/testgtk.c (file_exists): Fix stupid typo that was keeping RC file from being loaded. * gtk/testgtkrc gtk/testgtkrc2: Test new pango-ized RC file font code. Mon May 29 14:31:27 2000 Owen Taylor <otaylor@redhat.com> * gdk/gdkfont.h gdk/x11/gdkfont-x11.c (gdk_font_from_description): Add function to load a GdkFont from a PangoFontDescription. Fri May 26 17:16:40 2000 Owen Taylor <otaylor@redhat.com> * gtk/frame.[ch] gtkaspectframe.c: Make frame widgets able to have any widget for the label, use a GtkLabel widget to display the text. (Based partially on a patch from Anders Carlson.) (Quite a bit of code reorganization - strip 90% of the guts out of gtkaspectframe and add a single virtual func to GtkFrameClass - compute_child_allocation.) Fri May 26 12:00:02 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtkctree.c gtk/gtkclist.[ch]: Pangoized. (Removed clist->row_center_offset field because caching it wasn't saving time or code, added private function _gtk_clist_create_cell_layout()). Wed May 24 15:59:37 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtkaccellabel.c: Pangoized. * gtk/[hv]ruler.c: Pangoized Mon May 22 19:23:59 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtkfilesel.c (gtk_file_selection_init): Use gtk_clist_set_column_auto_resize() to remove need need for manual column width computations. Mon May 22 18:50:26 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtktooltips.[ch]: Replace custom drawing with a GtkLabel, ensuring Pango correctness, and considerably simplifying the code. * gtk/gtklabel.c gtk[hv]scale.c: 1000 => PANGO_SCALE. * gtk/gtklabel.c (gtk_label_size_request): Fixed incorrect getting of numbers of lines. * gtk/gtklabel.c (gtk_label_size_request): Set the requisition to the actual requested width of the lable, not to the wrap width we set. * gtk/gtktextchild.h: Remove extraneous include of gtk/gtk.h. * gtk/gtktextbtree.c gtk/gtktextbuffer.c gtk/gtktextlayout.c gtk/gtktextview.c gtk/gtktextview.[ch]: Fix up includes. * gtk/gtktextview.c: Fix structure inheritance. * gtk/gtkprogressbar.c: Pangoize. Mon May 22 15:47:30 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtktextview.c (gtk_text_view_get_first_para_iter): Encapsulate in a function. * gtk/gtktextlayout.c (find_display_line_above): Fixed bug with computing line tops. * gtk/gtktextview.c (changed_handler): Fix < , <= confusion. Thu May 18 18:53:31 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtktextdisplay.c (gtk_text_layout_draw): Fix up the x_offset and y_offset coordinates to do what we need now. (The offset between buffer and layout coordinates has been reintroduced, but is a bit different than before.) * gtk/gtktextview.[ch]: No longer inherit from GtkLayout; instead handle the adjustments ourselves, and scroll as necessary using the new gdk_window_scroll(). The advantage of this is that when we are incrementally revalidating, we are essentially rearranging things around the visible portion of the screen. With the old setup, the visible portion of the screen was moved around in the layout, so scrolling and redrawing to track that caused jumping of the display. Since we now control the scrolling ourselves, we can suppress this and only redraw when things actually change. Thu May 18 18:47:25 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtktextbtree.c (redisplay_mark): We need to invalidate the region not just redisplay it after-all, since we store the cursors in the LineDisplay. (Ugly interactions here between GtkLayout and GtkTextBTree here.) * gtk/gtktextbtree.c (redisplay_region): Fixed reversed comparison. Thu May 18 18:43:21 2000 Owen Taylor <otaylor@redhat.com> * gdk/gdkwindow.h gdk/x11/gdkgeometry-x11.c (gdk_window_scroll): Added function to scroll contents of a window while keeping the window constant. Works by XCopyArea or guffaw-scrolling depending on the details of how the window is set up. (guffaw-scrolling still needs to be filled in.) Wed May 17 22:36:53 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtktextiter.c gtk/gtkmain.c: Add a debug key for the text widget, move the debugging that was tied to a global variable to that. * gtk/gtkmarshal.list: Add NONE:INT,INT,INT * gtk/gtktextbtree.[ch] gtk/gtktextlayout.c: Keep a separate validated flag, in line data instead of setting height/width to -1. This allows us to perform operations with partially invalid buffer (using the old size for invalid lines) and thus to do incremental vaidation. Keep height/width aggregates up to date when deleting text and rebalancing the tree. * gtk/gtktextbtree.[ch]: Add functions validate a line (gtk_text_btree_validate_line), and to validate up to a number of pixels (gtk_text_btree_validate). * gtk/gtktextlayout.[ch]: Add an ::invalidated signal that indicates that something is changed and a revalidation pass is needed. Change ::need_repaint to ::changed, and make it take old and new yranges instead of a rectangle. * gtk/gtktextbtree.[ch] gtk/gtktextlayout.[ch]: Move the line_data_destroy() function from gtk_text_btree_add_view() to a virtual function in GtkTextLayout * gtk/gtktextbtree.[ch]: Remove gtk_text_btree_get_damage_range(), since we are handling partial repaints in a different fashion now. * gtk/gtktextbtree.[ch]: Only repaint the changed portion of the selection instead of queueing a repaint on the entire widget. * gtk/gtktextbuffer.[ch] gtk/gtktextbtree.[ch]: Move get_selection_bounds() down to btree, make the function in buffer a wrapper around the btree function. * gtk/gtktextlayout.[ch]: Add functions to check if the layout is valid and to recompute either a range of pixels aroudn a line or a certain total number of pixels. * gtk/gtktextlayout.[ch]: Cache a single line display; now that we only redraw the needed portions, the hit rate for this cache is quite high. * gtk/gtktextview.[ch]: Keep track of the first paragraph on the screen so that when re-laying-out the buffer, we can keep the same place. This requires connecting to ::value_changed on the adjustments * gtk/gtktextview.[ch]: Add idle functions to revalidate the buffer after we receive an ::invalidated signal. Wed May 17 22:10:47 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtklayout.c (gtk_layout_size_allocate): Set upper to max of allocation and layout size, not just to the layout size. * gtk/gtk[hv]scrollbar.c (gtk_[hv]scrollbar_calc_slider_size): Invalidate window so it gets redrawn properly. * gdk/gdkwindow.c (gdk_window_invalidate_rect): Allow rect == NULL to mean the entire window. * gdk/gdkevents.h: Move definition for GDK_PRIORITY_REDRAW into public header. Mon May 15 14:51:31 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtktextmark.c (gtk_text_mark_get_name): Add function to get the name of a mark. * gtk/gtktextlayout.c (gtk_text_layout_get_line_at_y): Add a function to find the paragraph from a y position. Thu May 11 12:57:20 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtktextbtree.c (gtk_text_btree_node_invalidate_upward): Valid nodes have width/height >= 0, not > 0. Tue May 9 21:29:06 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtktextlayout.[ch] gtk/gtktextdisplay.c (gtk_text_layout_get_line_display): Add a size_only flag, so when we only need the size, we don't create useless appearance attributes. * gtk/gtktextview.c (gtk_text_view_ensure_layout): Remove duplicate setting of font description. * gtk/gtkscale.c: Use PANGO_SCALE instead of 1000 Wed Apr 26 01:53:23 2000 Owen Taylor <otaylor@redhat.com> * gtk/Makefile.am (EXTRA_DIST): Add OLD_STAMP into EXTRA_DIST. It does not work well when the file that everything depends on is not in the tarball. Wed Apr 26 00:56:14 2000 Owen Taylor <otaylor@redhat.com> * gtk/testgtk.c: Some hacks and fixes so that it basically works when not sitting in the GTK+ build tree. 2000-05-03 Havoc Pennington <hp@redhat.com> * gtk/gtktextbtree.c (gtk_text_line_next_could_contain_tag): Properly determine the ordering of the tag root and the current line within the tree. Previous algorithm only worked if the tag root's immediate parent was the common root of both the current line and the tag root. Wed Apr 26 00:43:00 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtktextlayout.c (set_para_values): Fix some bugs in alignment. * gtk/gtktextview.c (gtk_text_view_ensure_layout): Track the widget text directional dynamically. * gtk/gtktextview.[ch]: Added functions to get and set default wrap mode. Tue Apr 25 23:47:38 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtktextlayout.c (gtk_text_layout_get_iter_location): Fix bug in cursor location computation. Tue Apr 25 23:22:59 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtklayout.c (gtk_layout_set_size): Clamp hadjustment/ vadjusment values properly when layout gets smaller. * gtk/gtktextview.c (need_repaint_handler): Areas being passed in are far completely inaccurate, and sometimes too small, so, for now, just queue a redraw on the whole visible region. 2000-04-25 Havoc Pennington <hp@redhat.com> * gtk/gtktextbtree.c (summary_destroy): new function to destroy tag summary nodes (gtk_text_line_next_could_contain_tag): this function was totally broken if the line passed in wasn't below the tag root. Fix it. (gtk_text_btree_first_could_contain_tag): In the tag == NULL "wildcard" case, we have to do a linear scan. Blah. (gtk_text_btree_last_could_contain_tag): In tag == NULL case, we have to do the linear scan (tag_removed_cb): When a tag is removed from the tag table, remove the GtkTextTagInfo node from the btree. (gtk_text_btree_spew): Implement the spew function, for our debugging pleasure. Tue Apr 25 19:40:18 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtktextlayout.c (gtk_text_layout_set_buffer): Fix a problem with referring to the wrong buffer. * gtk/gtkentry.c: Fix focus-in/focus-out confusion. * gtk/gtkrc.c gtk/gtkstyle.c: Moving setting default font description to gtk_style_new() - otherwise things don't work without a .gtkrc file. * gtk/gtktextbuffer.c (gtk_text_buffer_new): Sink the tags table if we create it ourself, too. * gdk/gdktypes.h (enum): Move GDK_RELEASE_MASK, since it was conflicting with XKB modifiers. * gtk/gtktextview.[ch]: Add simple support for GtkIMContext. Mon Apr 24 19:34:18 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtkentry.c (gtk_entry_move_cursor_visually): Fix problem with deletion from last commit. Mon Apr 24 19:29:40 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtkwidget.c (gtk_widget_create_pango_context): Set the language in the context from the current locale. * gtk/gtkentry.c (gtk_entry_size_request): Use language from the context, not hardcoded value. * gtk/gtkentry.c (gtk_entry_move_cursor): Make character movement visual, not logical. Sun Apr 23 23:39:18 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtkentry.c gtk/gtktextdisplay.c: Don't handle selections as attributes - that doesn't handle partial-glyph selection properly. Instead use new pango_layout_line_get_x_ranges() functionality to draw the selection. * gtk/gtkentry.c: Simplify code since pango_layout_line_index_to_x() now properly handles out-of-range coordinates. * gtk/gtktextbuffer.c: Emit "mark_set" when the cursor is moved. * gtk/gtktextiter.h gtk/gtktextiterprivate.h: Make gtk_text_iter_get_line_byte() public. * gtk/gtktextlayout.[ch]: Properly set the direction in the PangoContext for paragraphs opposite to the base direction of the widget. * gtk/gtktextlayout.[ch] gtk/gtktextdisplay.c: Fixes for alignment. * gtk/gtktextlayout.c: Don't split segments on marks, since that causes Arabic words to reshape as you cursor through. * gtk/gtktextlayout.[ch] gtk/gtktextview.[ch]: Implement virtual cursor position when moving vertically with the arrow keys and scrolling with page-up/page-down. (Arrow keys save only the X, scrolling saves both X and Y.) This means you can line-up / line-down or page-up / page-down without losing your place, and also that moving vertically with the cursor keys keeps the same X position, not the same character count: * gtk/gtktextlayout.[ch] gtk/gtktextview.[ch]: Make vertical arrow keys move by display lines, not paragraphs. Tue Apr 18 14:16:50 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtklayout.c: Make sure that the bin window is at least as big as the allocation. (Should we also make sure that the bin window is big enough to completely cover widget->window?) * gtk/gtktextview.c (gtk_text_view_get_visible_rect): Add function to get the onscreen rectangle. * gdk/x11/gdkwindow-x11.c (gdk_window_get_pointer): Correctly account for offsets in window coordinates. Sun Apr 16 16:13:27 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtkentry.c (gtk_entry_get_cursor_locations): Fix index/offset confusion. * gtk/gtktextview.c (gtk_text_view_ensure_layout): Set the default direction from the widget direction. * gtk/gtktexttag.c gtk/gtktexttagprivate.h (gtk_text_tag_set_arg): Add a "direction" attribute. * gtk/gtktextview.c: global s/tkxt/text_view/. * gtk/testtext.c: Added long block of text in Arabic, to test out the direction attributes. (Some problems with the shaping system for arabic become obvious - like the fact the cursor splits words into unjoined pieces.) Fri Apr 14 12:54:34 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtktextdisplay.c (render_layout): Add overstrike handling. * gtk/gtktextlayout.c: Fix up alignment. * gtk/testtext.c: Add some tests for centering, wrapping. Fri Apr 14 09:26:22 2000 Owen Taylor <otaylor@redhat.com> * gdk/gdkdrawable.h gdk/gdkdraw.c gdk/gdkwindow.c gdk/x11/gdkdrawable-x11.c: Add a draw_glyphs() operation to the drawable vtable and gdk_draw_glyphs(). If we wrote GTK+-specific layout-render function this could just replace the draw_layout() operation in the vtable. * gtk/gtkentry.c: Move guts of gtk_entry_get_cursor_locations to pango_layout_get_cursor_pos() and use that function. * gtk/gtktextchild.[ch]: add gtk_ onto pixmap_segment_new(), since it is a non-static symbol. * gtk/gtktextbtree.[ch]: Replace gtk_text_btree_find_line_data_by_y() with gtk_text_btree_find_line_by_y() * gtk/gtktextdisplay.c: Rewrote for Pango - uses a custom layout renderer that handles GtkTextAppearance attributes. * gtk/gtktexttag.[ch] gtk/gtktexttagprivate.h: - Move the values in the style that don't affect geometry into a GtkTextAppearance structure. - Change underline to take a PangoUnderline and "font" a string representation of a font description - Add a "font_desc" attribute which takes a FontDescription structure. * gtk/gtktextlayout.[ch]: - Get rid of the display-line list per each line. Instead, we generate, on demand, a GtkTextLineDisplay structure which] contains a PangoLayout * and other necesary information (offsets, cursor locations) for displaying a paragraph. - Get rid of the code to wrap lines, create display chunks, etc. Instead, we just go through a paragraph and convert it into the necessary inputs to a PangoLayout. - Implement a new attribute type, GtkTextAttrAppearance. This holds a GtkTextAppearance, and is used to pass colors, stipple, etc, through from the layout to the display without having to use lots and lots of individual attributes. - Reimplement gtk_layout_get_iter_at_pixel() gtk_layout_get_iter_pos() in terms of PangoLayout functions. * gtk/gtktextview.c: - Handle passing the necessary PangoContext to the layout - Some fixups in painting to deal with the automatic backing store and offsetting of GTK+-1.4 - Add a style_set handler so that the default style reacts properly to theme changes. * gtk/gtktext?*.[ch]: Random code-style fixes. * gtk/testtext.c: Substitute in languages that Pango handles now for Thai Mon Apr 10 10:33:45 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtktext?*.[ch]: Check in Havoc's port of the Tk text widget, in original form preparatory to Pango-ization and gdkimcontext-ization. Thu Apr 6 19:25:39 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtkimcontext.c: Move default implementations to real_* vfuncs, so that we can derive from gtkimcontext in language bindings properly. Thu Apr 6 16:02:52 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtkimcontextsimple.[ch]: Use gdk_keyval_to_unicode to gdk_unicode_to_keyval. Add a compose table including (almost) all the compose combinations from X. This is 6k of static, shared data as opposed to 50k or so of dynamic data in the XIM implementation. * gdk/gdk.h gdk/gdkkeyuni.c gdk/win32/gdkevents-win32.c (gdk_keyval_to_unicode, gdk_unicode_to_keyval): Moved functions to convert keyvalues from and to unicode here from the win32 port and made them public. Wed Apr 5 16:37:29 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtkeditable.c (gtk_editable_insert_text): Allow new_text_length == -1. Wed Apr 5 16:27:45 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtkimcontext.[ch]: Base class for new input context system * gtk/gtkimmulticontext.[ch]: Proxy input context that allows the real input context implementation to be loaded from modules and switched on the fly. * gtk/gtkcontextsimple.[ch]: Simple implementation of an input context that just does direct keysymbol => unicode translation. * gtk/gtkentry.[ch]: Start switching editing over to using GtkInputContext. (No handling of preedit yet.) Wed Apr 5 15:48:41 2000 Owen Taylor <otaylor@redhat.com> * gtk/gtktypeutils.h (GTK_CHECK_GET_CLASS): Fix problem with one too many substitutions. (klass should not be subsituted.) Wed Apr 5 00:18:14 2000 Owen Taylor <otaylor@redhat.com> * configure.in: Add checks for Pango * configure.in docs/Makefile.am: Add test for sgml2html and allow 'make dist' without building html, but print out warnings in that case. (For making snapshots) * gdk/Makefile.am gdk/x11/Makefile.am gtk/Makefile.am: Add Pango libraries and C flags * gdk/gdkdraw.c gdk/gdkdrawable.h gdkwindow.c gdk/x11/gdkdrawable-x11.c: Add function (gdk_draw_layout) to draw a pango layout. * gdk/gdkpango.h gdk/x11/gdkpango-x11.c: New file with functions for getting Pango contexts for GDK. * gtk/gtkeditable.c: Get rid of dead code gtk_editable_parent_set() * gtk/gtkentry.[ch]: Complete rewrite to use Pango, add bidirectional editing. * gtk/gtkentry.c: Hack in simple Hebrew input with direct keysym => unicode translations. More languages can be added here, but real input-method support is needed. * docs/Changes-1.4.txt: Added note about entry behavior. * gtk/gtkenums.h gtk/gtkwidget.[ch] testgtk.c gtkprivate.h: Add functions to set the reading direction for a widget and the global direction. Add test which allows toggling the global direction. Two private flags are used to store the direction. (GTK_DIRECTION_SET + GTK_DIRECTION_LTR) * gtk/gtkcheckbutton.c gtk/gtkframe.c gtk/gtkhbbox.c gtk/gtkhbox.c gtk/gtkradiobutton.c gtk/gtkspinbutton.c gtk/gtktable.c * gtk/gtk[hv]scale.c gtk/gtkscale.[ch]: Draw numbers using Pango * gtk/gtklabel.[ch]: Moved to Pango and considerably rewritten. Line breaking, underlining now handled by Pango. * gtk/gtkstyle.[ch] gtk/gtkrc.[ch]: Add a PangoFontDescription to RCStyle and Style. (Having both this and the old font name and GdkFont is temporary.) * gtk/gtkwidget.[ch] (gtk_widget_create_pango_{context,layout}): Added convenience functions for creating contexts and layouts for widgets. * gtk/testgtk.c: Enhance label tests with multilingual labels.
Diffstat (limited to 'gtk')
-rw-r--r--gtk/.cvsignore3
-rw-r--r--gtk/Makefile.am55
-rw-r--r--gtk/gtk.h2
-rw-r--r--gtk/gtkaccellabel.c28
-rw-r--r--gtk/gtkaspectframe.c236
-rw-r--r--gtk/gtkcalendar.c213
-rw-r--r--gtk/gtkcheckbutton.c9
-rw-r--r--gtk/gtkclist.c2084
-rw-r--r--gtk/gtkclist.h6
-rw-r--r--gtk/gtkctree.c1531
-rw-r--r--gtk/gtkdebug.h3
-rw-r--r--gtk/gtkeditable.c32
-rw-r--r--gtk/gtkentry.c1286
-rw-r--r--gtk/gtkentry.h33
-rw-r--r--gtk/gtkenums.h8
-rw-r--r--gtk/gtkfilesel.c47
-rw-r--r--gtk/gtkfontsel.c3452
-rw-r--r--gtk/gtkfontsel.h140
-rw-r--r--gtk/gtkframe.c491
-rw-r--r--gtk/gtkframe.h32
-rw-r--r--gtk/gtkhbbox.c8
-rw-r--r--gtk/gtkhbox.c9
-rw-r--r--gtk/gtkhruler.c43
-rw-r--r--gtk/gtkhscale.c68
-rw-r--r--gtk/gtkhscrollbar.c5
-rw-r--r--gtk/gtkimcontext.c259
-rw-r--r--gtk/gtkimcontext.h87
-rw-r--r--gtk/gtkimcontextsimple.c932
-rw-r--r--gtk/gtkimcontextsimple.h63
-rw-r--r--gtk/gtkimmulticontext.c247
-rw-r--r--gtk/gtkimmulticontext.h61
-rw-r--r--gtk/gtkinvisible.c19
-rw-r--r--gtk/gtkinvisible.h1
-rw-r--r--gtk/gtklabel.c802
-rw-r--r--gtk/gtklabel.h35
-rw-r--r--gtk/gtklayout.c51
-rw-r--r--gtk/gtkmain.c3
-rw-r--r--gtk/gtkmarshal.list2
-rw-r--r--gtk/gtkmarshalers.list2
-rw-r--r--gtk/gtkprivate.h10
-rw-r--r--gtk/gtkprogressbar.c57
-rw-r--r--gtk/gtkradiobutton.c3
-rw-r--r--gtk/gtkrc.c102
-rw-r--r--gtk/gtkrc.h4
-rw-r--r--gtk/gtkscale.c67
-rw-r--r--gtk/gtkscale.h5
-rw-r--r--gtk/gtkspinbutton.c14
-rw-r--r--gtk/gtkstyle.c12
-rw-r--r--gtk/gtkstyle.h4
-rw-r--r--gtk/gtktable.c5
-rw-r--r--gtk/gtktextbtree.c6349
-rw-r--r--gtk/gtktextbtree.h279
-rw-r--r--gtk/gtktextbuffer.c1652
-rw-r--r--gtk/gtktextbuffer.h309
-rw-r--r--gtk/gtktextchild.c1304
-rw-r--r--gtk/gtktextchild.h24
-rw-r--r--gtk/gtktextdisplay.c609
-rw-r--r--gtk/gtktextdisplay.h39
-rw-r--r--gtk/gtktextiter.c2384
-rw-r--r--gtk/gtktextiter.h182
-rw-r--r--gtk/gtktextiterprivate.h32
-rw-r--r--gtk/gtktextlayout.c2159
-rw-r--r--gtk/gtktextlayout.h251
-rw-r--r--gtk/gtktextmark.c339
-rw-r--r--gtk/gtktextmark.h24
-rw-r--r--gtk/gtktextmarkprivate.h37
-rw-r--r--gtk/gtktextsegment.c626
-rw-r--r--gtk/gtktextsegment.h128
-rw-r--r--gtk/gtktexttag.c1272
-rw-r--r--gtk/gtktexttag.h114
-rw-r--r--gtk/gtktexttagprivate.h117
-rw-r--r--gtk/gtktexttagtable.c252
-rw-r--r--gtk/gtktexttagtable.h53
-rw-r--r--gtk/gtktexttypes.c250
-rw-r--r--gtk/gtktexttypes.h150
-rw-r--r--gtk/gtktextview.c2899
-rw-r--r--gtk/gtktextview.h151
-rw-r--r--gtk/gtktooltips.c187
-rw-r--r--gtk/gtktooltips.h4
-rw-r--r--gtk/gtkvruler.c41
-rw-r--r--gtk/gtkvscale.c64
-rw-r--r--gtk/gtkvscrollbar.c5
-rw-r--r--gtk/gtkwidget.c190
-rw-r--r--gtk/gtkwidget.h13
-rw-r--r--gtk/testcalendar.c422
-rw-r--r--gtk/testgtk.c154
-rw-r--r--gtk/testgtkrc7
-rw-r--r--gtk/testgtkrc22
-rw-r--r--gtk/testtext.c1261
89 files changed, 29505 insertions, 7501 deletions
diff --git a/gtk/.cvsignore b/gtk/.cvsignore
index af39208db..78501f4c5 100644
--- a/gtk/.cvsignore
+++ b/gtk/.cvsignore
@@ -7,8 +7,9 @@ _libs
libgtk-1.1.la
testgtk
testinput
-testselection
testrgb
+testselection
+testtext
simple
testtree
gtkcompat.h
diff --git a/gtk/Makefile.am b/gtk/Makefile.am
index 13caa30a9..c1c40aabd 100644
--- a/gtk/Makefile.am
+++ b/gtk/Makefile.am
@@ -13,6 +13,7 @@ INCLUDES = @STRIP_BEGIN@ \
@GTK_DEBUG_FLAGS@ \
@GTK_XIM_FLAGS@ \
@GTK_LOCALE_FLAGS@ \
+ @PANGO_CFLAGS@ \
@GLIB_CFLAGS@ \
@x_cflags@ \
@STRIP_END@
@@ -29,6 +30,7 @@ libgtk_la_LDFLAGS = @STRIP_BEGIN@ \
-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
-release $(LT_RELEASE) \
-export-dynamic \
+ @PANGO_LIBS@ \
@GLIB_DEPLIBS@ \
@x_ldflags@ \
@x_libs@ \
@@ -94,6 +96,8 @@ gtk_public_h_sources = @STRIP_BEGIN@ \
gtkhseparator.h \
gtkhsv.h \
gtkimage.h \
+ gtkimcontext.h \
+ gtkimmulticontext.h \
gtkinputdialog.h \
gtkinvisible.h \
gtkitem.h \
@@ -137,6 +141,12 @@ gtk_public_h_sources = @STRIP_BEGIN@ \
gtkstatusbar.h \
gtktable.h \
gtktearoffmenuitem.h \
+ gtktextbuffer.h \
+ gtktextiter.h \
+ gtktextmark.h \
+ gtktexttag.h \
+ gtktexttagtable.h \
+ gtktextview.h \
gtktext.h \
gtkthemes.h \
gtktipsquery.h \
@@ -157,6 +167,23 @@ gtk_public_h_sources = @STRIP_BEGIN@ \
gtkwidget.h \
gtkwindow.h \
@STRIP_END@
+
+#
+# GTK+ header files that we install, but don't extract enums from
+# (This class is iffy; we have it for the semi-public interface
+# of the Text widget, which is useful for writing new view types
+# such as a Canvas text item, but isn't part of the clean public
+# interface)
+
+gtk_semipublic_h_sources = @STRIP_BEGIN@ \
+ gtktextbtree.h \
+ gtktextchild.h \
+ gtktextdisplay.h \
+ gtktextlayout.h \
+ gtktextsegment.h \
+ gtktexttypes.h \
+@STRIP_END@
+
# GTK+ header files that don't get installed
gtk_private_h_sources = @STRIP_BEGIN@ \
@STRIP_END@
@@ -207,6 +234,10 @@ gtk_c_sources = @STRIP_BEGIN@ \
gtkhseparator.c \
gtkhsv.c \
gtkimage.c \
+ gtkimcontext.c \
+ gtkimcontextsimple.c \
+ gtkimcontextsimple.h \
+ gtkimmulticontext.c \
gtkinputdialog.c \
gtkintl.h \
gtkinvisible.c \
@@ -252,6 +283,21 @@ gtk_c_sources = @STRIP_BEGIN@ \
gtktable.c \
gtktearoffmenuitem.c \
gtktext.c \
+ gtktextbtree.c \
+ gtktextbuffer.c \
+ gtktextchild.c \
+ gtktextdisplay.c \
+ gtktextiter.c \
+ gtktextiterprivate.h \
+ gtktextlayout.c \
+ gtktextmark.c \
+ gtktextmarkprivate.h \
+ gtktextsegment.c \
+ gtktexttag.c \
+ gtktexttagprivate.h \
+ gtktexttagtable.c \
+ gtktexttypes.c \
+ gtktextview.c \
gtkthemes.c \
gtktipsquery.c \
gtktogglebutton.c \
@@ -336,7 +382,7 @@ gdk_headers = @STRIP_BEGIN@ \
#
# setup GTK+ sources and their dependancies
#
-libgtkinclude_HEADERS = $(gtk_public_h_sources) $(gtk_built_public_sources)
+libgtkinclude_HEADERS = $(gtk_public_h_sources) $(gtk_semipublic_h_sources) $(gtk_built_public_sources)
libgtk_la_SOURCES = $(gtk_c_sources)
MAINTAINERCLEANFILES += $(gtk_built_public_sources) $(gtk_built_sources)
EXTRA_HEADERS +=
@@ -441,7 +487,7 @@ uninstall-local:
#
# test programs, not to be installed
#
-noinst_PROGRAMS = testgtk testinput testselection testrgb testdnd simple # testthreads
+noinst_PROGRAMS = testgtk testcalendar testinput testselection testrgb testdnd testtext simple # testthreads
DEPS = libgtk.la $(top_builddir)/gdk/libgdk.la
LDADDS = @STRIP_BEGIN@ \
libgtk.la \
@@ -449,20 +495,25 @@ LDADDS = @STRIP_BEGIN@ \
@x_ldflags@ \
@x_libs@ \
@GDK_WLIBS@ \
+ @PANGO_LIBS@ \
@GLIB_LIBS@ \
@GTK_LIBS_EXTRA@ \
-lm \
@STRIP_END@
testgtk_DEPENDENCIES = $(DEPS)
+testcalendar_DEPENDENCIES = $(DEPS)
testinput_DEPENDENCIES = $(DEPS)
testselection_DEPENDENCIES = $(DEPS)
testrgb_DEPENDENCIES = $(DEPS)
+testtext_DEPENDENCIES = $(DEPS)
testdnd_DEPENDENCIES = $(DEPS)
simple_DEPENDENCIES = $(DEPS)
#testthreads_DEPENDENCIES = $(DEPS)
+testcalendar_LDADD = $(LDADDS)
testgtk_LDADD = $(LDADDS)
testinput_LDADD = $(LDADDS)
testselection_LDADD = $(LDADDS)
+testtext_LDADD = $(LDADDS)
testrgb_LDADD = $(LDADDS)
testdnd_LDADD = $(LDADDS)
simple_LDADD = $(LDADDS)
diff --git a/gtk/gtk.h b/gtk/gtk.h
index 6c1079fde..df6dfb0f6 100644
--- a/gtk/gtk.h
+++ b/gtk/gtk.h
@@ -117,6 +117,8 @@
#include <gtk/gtktable.h>
#include <gtk/gtktearoffmenuitem.h>
#include <gtk/gtktext.h>
+#include <gtk/gtktextbuffer.h>
+#include <gtk/gtktextview.h>
#include <gtk/gtkthemes.h>
#include <gtk/gtktipsquery.h>
#include <gtk/gtktogglebutton.h>
diff --git a/gtk/gtkaccellabel.c b/gtk/gtkaccellabel.c
index 665396aa2..4b82736f9 100644
--- a/gtk/gtkaccellabel.c
+++ b/gtk/gtkaccellabel.c
@@ -230,6 +230,8 @@ gtk_accel_label_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
GtkAccelLabel *accel_label;
+ PangoLayout *layout;
+ PangoRectangle logical_rect;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_ACCEL_LABEL (widget));
@@ -239,9 +241,13 @@ gtk_accel_label_size_request (GtkWidget *widget,
if (GTK_WIDGET_CLASS (parent_class)->size_request)
GTK_WIDGET_CLASS (parent_class)->size_request (widget, requisition);
+
+ layout = gtk_widget_create_pango_layout (widget);
+ pango_layout_set_text (layout, accel_label->accel_string, -1);
+ pango_layout_get_extents (layout, NULL, &logical_rect);
- accel_label->accel_string_width = gdk_string_width (GTK_WIDGET (accel_label)->style->font,
- accel_label->accel_string);
+ accel_label->accel_string_width = logical_rect.width / PANGO_SCALE;
+ pango_layout_unref (layout);
}
static gint
@@ -250,6 +256,7 @@ gtk_accel_label_expose_event (GtkWidget *widget,
{
GtkMisc *misc;
GtkAccelLabel *accel_label;
+ PangoLayout *layout;
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_ACCEL_LABEL (widget), FALSE);
@@ -279,22 +286,25 @@ gtk_accel_label_expose_event (GtkWidget *widget,
y = (widget->allocation.y * (1.0 - misc->yalign) +
(widget->allocation.y + widget->allocation.height -
(widget->requisition.height - misc->ypad * 2)) *
- misc->yalign + widget->style->font->ascent) + 1.5;
+ misc->yalign) + 1.5;
+
+ layout = gtk_widget_create_pango_layout (widget);
+ pango_layout_set_text (layout, accel_label->accel_string, -1);
if (GTK_WIDGET_STATE (accel_label) == GTK_STATE_INSENSITIVE)
- gdk_draw_string (widget->window,
- widget->style->font,
+ gdk_draw_layout (widget->window,
widget->style->white_gc,
x + 1,
y + 1,
- accel_label->accel_string);
+ layout);
- gdk_draw_string (widget->window,
- widget->style->font,
+ gdk_draw_layout (widget->window,
widget->style->fg_gc[GTK_WIDGET_STATE (accel_label)],
x,
y,
- accel_label->accel_string);
+ layout);
+
+ pango_layout_unref (layout);
}
else
{
diff --git a/gtk/gtkaspectframe.c b/gtk/gtkaspectframe.c
index 3d084b1c9..8040a1471 100644
--- a/gtk/gtkaspectframe.c
+++ b/gtk/gtkaspectframe.c
@@ -23,7 +23,7 @@
*/
/*
- * Modified by the GTK+ Team and others 1997-1999. See the AUTHORS
+ * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
@@ -39,26 +39,22 @@ enum {
ARG_OBEY_CHILD
};
-static void gtk_aspect_frame_class_init (GtkAspectFrameClass *klass);
-static void gtk_aspect_frame_init (GtkAspectFrame *aspect_frame);
-static void gtk_aspect_frame_set_arg (GtkObject *object,
- GtkArg *arg,
- guint arg_id);
-static void gtk_aspect_frame_get_arg (GtkObject *object,
- GtkArg *arg,
- guint arg_id);
-static void gtk_aspect_frame_draw (GtkWidget *widget,
- GdkRectangle *area);
-static void gtk_aspect_frame_paint (GtkWidget *widget,
- GdkRectangle *area);
-static gint gtk_aspect_frame_expose (GtkWidget *widget,
- GdkEventExpose *event);
-static void gtk_aspect_frame_size_allocate (GtkWidget *widget,
- GtkAllocation *allocation);
+static void gtk_aspect_frame_class_init (GtkAspectFrameClass *klass);
+static void gtk_aspect_frame_init (GtkAspectFrame *aspect_frame);
+static void gtk_aspect_frame_set_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id);
+static void gtk_aspect_frame_get_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id);
+static void gtk_aspect_frame_compute_child_allocation (GtkFrame *frame,
+ GtkAllocation *child_allocation);
#define MAX_RATIO 10000.0
#define MIN_RATIO 0.0001
+static GtkFrameClass *parent_class = NULL;
+
GtkType
gtk_aspect_frame_get_type (void)
{
@@ -88,17 +84,17 @@ static void
gtk_aspect_frame_class_init (GtkAspectFrameClass *class)
{
GtkObjectClass *object_class;
- GtkWidgetClass *widget_class;
+ GtkFrameClass *frame_class;
+ parent_class = gtk_type_class (GTK_TYPE_FRAME);
+
object_class = GTK_OBJECT_CLASS (class);
- widget_class = GTK_WIDGET_CLASS (class);
+ frame_class = GTK_FRAME_CLASS (class);
object_class->set_arg = gtk_aspect_frame_set_arg;
object_class->get_arg = gtk_aspect_frame_get_arg;
- widget_class->draw = gtk_aspect_frame_draw;
- widget_class->expose_event = gtk_aspect_frame_expose;
- widget_class->size_allocate = gtk_aspect_frame_size_allocate;
+ frame_class->compute_child_allocation = gtk_aspect_frame_compute_child_allocation;
gtk_object_add_arg_type ("GtkAspectFrame::xalign", GTK_TYPE_FLOAT,
GTK_ARG_READWRITE, ARG_XALIGN);
@@ -117,10 +113,6 @@ gtk_aspect_frame_init (GtkAspectFrame *aspect_frame)
aspect_frame->yalign = 0.5;
aspect_frame->ratio = 1.0;
aspect_frame->obey_child = TRUE;
- aspect_frame->center_allocation.x = -1;
- aspect_frame->center_allocation.y = -1;
- aspect_frame->center_allocation.width = 1;
- aspect_frame->center_allocation.height = 1;
}
static void
@@ -243,157 +235,17 @@ gtk_aspect_frame_set (GtkAspectFrame *aspect_frame,
}
static void
-gtk_aspect_frame_paint (GtkWidget *widget,
- GdkRectangle *area)
-{
- GtkFrame *frame;
- gint height_extra;
- gint label_area_width;
- gint x, y, x2, y2;
- GtkAllocation *allocation;
-
- g_return_if_fail (widget != NULL);
- g_return_if_fail (GTK_IS_ASPECT_FRAME (widget));
- g_return_if_fail (area != NULL);
-
- if (GTK_WIDGET_DRAWABLE (widget))
- {
- frame = GTK_FRAME (widget);
- allocation = &GTK_ASPECT_FRAME(widget)->center_allocation;
-
- height_extra = frame->label_height - widget->style->klass->xthickness;
- height_extra = MAX (height_extra, 0);
-
- x = GTK_CONTAINER (frame)->border_width;
- y = GTK_CONTAINER (frame)->border_width;
-
- if (frame->label)
- {
- label_area_width = (allocation->width +
- GTK_CONTAINER (frame)->border_width * 2 -
- widget->style->klass->xthickness * 2);
-
- x2 = ((label_area_width - frame->label_width) * frame->label_xalign +
- GTK_CONTAINER (frame)->border_width + widget->style->klass->xthickness);
- y2 = (GTK_CONTAINER (frame)->border_width + widget->style->font->ascent);
-
- gtk_paint_shadow_gap (widget->style, widget->window,
- GTK_STATE_NORMAL, frame->shadow_type,
- area, widget, "frame",
- allocation->x + x,
- allocation->y + y + height_extra / 2,
- allocation->width - x * 2,
- allocation->height - y * 2 - height_extra / 2,
- GTK_POS_TOP,
- x2 + 2 - x, frame->label_width - 4);
-
- gtk_paint_string (widget->style, widget->window, GTK_WIDGET_STATE (widget),
- area, widget, "frame",
- allocation->x + x2 + 3,
- allocation->y + y2,
- frame->label);
- }
- else
- gtk_paint_shadow (widget->style, widget->window,
- GTK_STATE_NORMAL, frame->shadow_type,
- area, widget, "frame",
- allocation->x + x,
- allocation->y + y + height_extra / 2,
- allocation->width - x * 2,
- allocation->height - y * 2 - height_extra / 2);
- }
-}
-
-/* the only modification to the next two routines is to call
- gtk_aspect_frame_paint instead of gtk_frame_paint */
-
-static void
-gtk_aspect_frame_draw (GtkWidget *widget,
- GdkRectangle *area)
+gtk_aspect_frame_compute_child_allocation (GtkFrame *frame,
+ GtkAllocation *child_allocation)
{
- GtkBin *bin;
- GdkRectangle child_area;
-
- g_return_if_fail (widget != NULL);
- g_return_if_fail (GTK_IS_ASPECT_FRAME (widget));
- g_return_if_fail (area != NULL);
-
- if (GTK_WIDGET_DRAWABLE (widget))
- {
- bin = GTK_BIN (widget);
-
- gtk_aspect_frame_paint (widget, area);
-
- if (bin->child && gtk_widget_intersect (bin->child, area, &child_area))
- gtk_widget_draw (bin->child, &child_area);
- }
-}
-
-static gint
-gtk_aspect_frame_expose (GtkWidget *widget,
- GdkEventExpose *event)
-{
- GtkBin *bin;
- GdkEventExpose child_event;
-
- g_return_val_if_fail (widget != NULL, FALSE);
- g_return_val_if_fail (GTK_IS_ASPECT_FRAME (widget), FALSE);
- g_return_val_if_fail (event != NULL, FALSE);
-
- if (GTK_WIDGET_DRAWABLE (widget))
- {
- bin = GTK_BIN (widget);
-
- gtk_aspect_frame_paint (widget, &event->area);
-
- child_event = *event;
- if (bin->child &&
- GTK_WIDGET_NO_WINDOW (bin->child) &&
- gtk_widget_intersect (bin->child, &event->area, &child_event.area))
- gtk_widget_event (bin->child, (GdkEvent*) &child_event);
- }
-
- return FALSE;
-}
-
-static void
-gtk_aspect_frame_size_allocate (GtkWidget *widget,
- GtkAllocation *allocation)
-{
- GtkFrame *frame;
- GtkAspectFrame *aspect_frame;
- GtkBin *bin;
-
- GtkAllocation child_allocation;
- gint x,y;
- gint width,height;
+ GtkAspectFrame *aspect_frame = GTK_ASPECT_FRAME (frame);
+ GtkBin *bin = GTK_BIN (frame);
gdouble ratio;
- g_return_if_fail (widget != NULL);
- g_return_if_fail (GTK_IS_ASPECT_FRAME (widget));
- g_return_if_fail (allocation != NULL);
-
- aspect_frame = GTK_ASPECT_FRAME (widget);
- frame = GTK_FRAME (widget);
- bin = GTK_BIN (widget);
-
- if (GTK_WIDGET_DRAWABLE (widget) &&
- ((widget->allocation.x != allocation->x) ||
- (widget->allocation.y != allocation->y) ||
- (widget->allocation.width != allocation->width) ||
- (widget->allocation.height != allocation->height)) &&
- (widget->allocation.width != 0) &&
- (widget->allocation.height != 0))
- gdk_window_clear_area (widget->window,
- widget->allocation.x,
- widget->allocation.y,
- widget->allocation.width,
- widget->allocation.height);
-
- widget->allocation = *allocation;
-
if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
{
+ GtkAllocation full_allocation;
+
if (aspect_frame->obey_child)
{
GtkRequisition child_requisition;
@@ -413,43 +265,21 @@ gtk_aspect_frame_size_allocate (GtkWidget *widget,
}
else
ratio = aspect_frame->ratio;
+
+ parent_class->compute_child_allocation (frame, &full_allocation);
- x = (GTK_CONTAINER (frame)->border_width +
- GTK_WIDGET (frame)->style->klass->xthickness);
- width = allocation->width - x * 2;
-
- y = (GTK_CONTAINER (frame)->border_width +
- MAX (frame->label_height, GTK_WIDGET (frame)->style->klass->ythickness));
- height = (allocation->height - y -
- GTK_CONTAINER (frame)->border_width -
- GTK_WIDGET (frame)->style->klass->ythickness);
-
- /* make sure we don't allocate a negative width or height,
- * since that will be cast to a (very big) guint16 */
- width = MAX (1, width);
- height = MAX (1, height);
-
- if (ratio * height > width)
+ if (ratio * full_allocation.height > full_allocation.width)
{
- child_allocation.width = width;
- child_allocation.height = width/ratio + 0.5;
+ child_allocation->width = full_allocation.width;
+ child_allocation->height = full_allocation.width / ratio + 0.5;
}
else
{
- child_allocation.width = ratio*height + 0.5;
- child_allocation.height = height;
+ child_allocation->width = ratio * full_allocation.height + 0.5;
+ child_allocation->height = full_allocation.height;
}
- child_allocation.x = aspect_frame->xalign * (width - child_allocation.width) + allocation->x + x;
- child_allocation.y = aspect_frame->yalign * (height - child_allocation.height) + allocation->y + y;
-
- aspect_frame->center_allocation.width = child_allocation.width + 2*x;
- aspect_frame->center_allocation.x = child_allocation.x - x;
- aspect_frame->center_allocation.height = child_allocation.height + y +
- GTK_CONTAINER (frame)->border_width +
- GTK_WIDGET (frame)->style->klass->ythickness;
- aspect_frame->center_allocation.y = child_allocation.y - y;
-
- gtk_widget_size_allocate (bin->child, &child_allocation);
+ child_allocation->x = full_allocation.x + aspect_frame->xalign * (full_allocation.width - child_allocation->width);
+ child_allocation->y = full_allocation.y + aspect_frame->yalign * (full_allocation.height - child_allocation->height);
}
}
diff --git a/gtk/gtkcalendar.c b/gtk/gtkcalendar.c
index 1f220bda4..bc5708dfc 100644
--- a/gtk/gtkcalendar.c
+++ b/gtk/gtkcalendar.c
@@ -1155,20 +1155,20 @@ gtk_calendar_size_request (GtkWidget *widget,
{
GtkCalendar *calendar;
GtkCalendarPrivateData *private_data;
+ PangoLayout *layout;
+ PangoRectangle logical_rect;
gint height;
gint i;
gchar buffer[255];
gint calendar_margin = CALENDAR_MARGIN;
gint header_width, main_width;
- gint lbearing;
- gint rbearing;
- gint ascent;
- gint descent;
- gint width;
+ gint max_header_height = 0;
calendar = GTK_CALENDAR (widget);
private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
+
+ layout = gtk_widget_create_pango_layout (widget);
/*
* Calculate the requisition width for the widget.
@@ -1181,17 +1181,21 @@ gtk_calendar_size_request (GtkWidget *widget,
private_data->max_month_width = 0;
for (i = 0; i < 12; i++)
{
+ pango_layout_set_text (layout, default_monthname[i], -1);
+ pango_layout_get_extents (layout, NULL, &logical_rect);
private_data->max_month_width = MAX (private_data->max_month_width,
- gdk_string_measure (HEADER_FONT (widget),
- default_monthname[i]) + 8);
+ logical_rect.width / PANGO_SCALE + 8);
+ max_header_height = MAX (max_header_height, logical_rect.height / PANGO_SCALE);
}
private_data->max_year_width = 0;
for (i=0; i<10; i++)
{
sprintf (buffer, "%d%d%d%d", i,i,i,i);
+ pango_layout_set_text (layout, buffer, -1);
+ pango_layout_get_extents (layout, NULL, &logical_rect);
private_data->max_year_width = MAX (private_data->max_year_width,
- gdk_string_measure (HEADER_FONT (widget),
- buffer) + 8);
+ logical_rect.width / PANGO_SCALE + 8);
+ max_header_height = MAX (max_header_height, logical_rect.height / PANGO_SCALE);
}
}
else
@@ -1215,9 +1219,15 @@ gtk_calendar_size_request (GtkWidget *widget,
for (i = 0; i < 9; i++)
{
sprintf (buffer, "%d%d", i, i);
+ pango_layout_set_text (layout, buffer, -1);
+ pango_layout_get_extents (layout, NULL, &logical_rect);
private_data->min_day_width = MAX (private_data->max_day_char_width,
- gdk_string_measure (DAY_FONT (widget),
- buffer));
+ logical_rect.width / PANGO_SCALE);
+
+ private_data->max_day_char_ascent = MAX (private_data->max_label_char_ascent,
+ PANGO_ASCENT (logical_rect) / PANGO_SCALE);
+ private_data->max_day_char_descent = MAX (private_data->max_label_char_descent,
+ PANGO_DESCENT (logical_rect) / PANGO_SCALE);
}
/* We add one to max_day_char_width to be able to make the marked day "bold" */
private_data->max_day_char_width = private_data->min_day_width / 2 +1;
@@ -1225,19 +1235,14 @@ gtk_calendar_size_request (GtkWidget *widget,
if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
for (i = 0; i < 7; i++)
{
- gdk_text_extents (LABEL_FONT (widget),
- default_abbreviated_dayname[i],
- strlen(default_abbreviated_dayname[i]),
- &lbearing,
- &rbearing,
- &width,
- &ascent,
- &descent);
- private_data->min_day_width = MAX (private_data->min_day_width, width);
- private_data->max_label_char_ascent = MAX (private_data->max_label_char_ascent,
- ascent);
+ pango_layout_set_text (layout, default_abbreviated_dayname[i], -1);
+ pango_layout_line_get_extents (pango_layout_get_lines (layout)->data, NULL, &logical_rect);
+
+ private_data->min_day_width = MAX (private_data->min_day_width, logical_rect.width / PANGO_SCALE);
+ private_data->max_label_char_ascent = MAX (private_data->max_label_char_ascent,
+ PANGO_ASCENT (logical_rect) / PANGO_SCALE);
private_data->max_label_char_descent = MAX (private_data->max_label_char_descent,
- descent);
+ PANGO_DESCENT (logical_rect) / PANGO_SCALE);
}
private_data->max_week_char_width = 0;
@@ -1245,8 +1250,10 @@ gtk_calendar_size_request (GtkWidget *widget,
for (i = 0; i < 9; i++)
{
sprintf (buffer, "%d%d", i, i);
+ pango_layout_set_text (layout, buffer, -1);
+ pango_layout_get_extents (layout, NULL, &logical_rect);
private_data->max_week_char_width = MAX (private_data->max_week_char_width,
- gdk_string_measure (LABEL_FONT (widget), buffer) / 2);
+ logical_rect.width / PANGO_SCALE / 2);
}
main_width = (7 * (private_data->min_day_width + DAY_XPAD * 2) + (DAY_XSEP * 6) + CALENDAR_MARGIN * 2
@@ -1263,9 +1270,7 @@ gtk_calendar_size_request (GtkWidget *widget,
if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
{
- private_data->header_h = (HEADER_FONT (widget)->ascent
- + HEADER_FONT (widget)->descent
- + CALENDAR_YSEP * 2);
+ private_data->header_h = (max_header_height + CALENDAR_YSEP * 2);
}
else
{
@@ -1284,15 +1289,6 @@ gtk_calendar_size_request (GtkWidget *widget,
private_data->day_name_h = 0;
}
- gdk_text_extents (DAY_FONT (widget),
- "0123456789",
- 10,
- &lbearing,
- &rbearing,
- &width,
- &private_data->max_day_char_ascent,
- &private_data->max_day_char_descent);
-
private_data->main_h = (CALENDAR_MARGIN + calendar_margin
+ 6 * (private_data->max_day_char_ascent
+ private_data->max_day_char_descent
@@ -1317,6 +1313,8 @@ gtk_calendar_size_request (GtkWidget *widget,
+ private_data->main_h);
requisition->height = height + (widget->style->klass->ythickness + INNER_BORDER) * 2;
+
+ pango_layout_unref (layout);
}
static void
@@ -1529,12 +1527,13 @@ gtk_calendar_paint_header (GtkWidget *widget)
GtkCalendar *calendar;
GdkGC *gc;
char buffer[255];
- int y, y_arrow;
+ int x, y;
gint header_width, cal_height;
- gint str_width;
gint max_month_width;
gint max_year_width;
GtkCalendarPrivateData *private_data;
+ PangoLayout *layout;
+ PangoRectangle logical_rect;
calendar = GTK_CALENDAR (widget);
private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
@@ -1562,40 +1561,39 @@ gtk_calendar_paint_header (GtkWidget *widget)
0, 0, header_width, private_data->header_h);
+ layout = gtk_widget_create_pango_layout (widget);
+
+ sprintf (buffer, "%d", calendar->year);
+ pango_layout_set_text (layout, buffer, -1);
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+
/* Draw title */
- y = private_data->header_h - (private_data->header_h
- - HEADER_FONT (widget)->ascent
- + HEADER_FONT (widget)->descent) / 2;
- y_arrow = (private_data->header_h - 9) / 2;
+ y = (private_data->header_h - logical_rect.height / PANGO_SCALE) / 2;
/* Draw year and its arrows */
- sprintf (buffer, "%d", calendar->year);
- str_width = gdk_string_measure (HEADER_FONT (widget), buffer);
- gdk_gc_set_foreground (gc, HEADER_FG_COLOR (GTK_WIDGET (calendar)));
+
if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
- gdk_draw_string (private_data->header_win, HEADER_FONT (widget), gc,
- header_width - (3 + max_year_width
- - (max_year_width - str_width)/2),
- y, buffer);
+ x = header_width - (3 + max_year_width
+ - (max_year_width - logical_rect.width / PANGO_SCALE)/2);
else
- gdk_draw_string (private_data->header_win, HEADER_FONT (widget), gc,
- header_width - (3 + private_data->arrow_width + max_year_width
- - (max_year_width - str_width)/2),
- y, buffer);
+ x = header_width - (3 + private_data->arrow_width + max_year_width
+ - (max_year_width - logical_rect.width / PANGO_SCALE)/2);
+
+
+ gdk_gc_set_foreground (gc, HEADER_FG_COLOR (GTK_WIDGET (calendar)));
+ gdk_draw_layout (private_data->header_win, gc, x, y, layout);
/* Draw month */
sprintf (buffer, "%s", default_monthname[calendar->month]);
- str_width = gdk_string_measure (HEADER_FONT (widget), buffer);
+ pango_layout_set_text (layout, buffer, -1);
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+
if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
- gdk_draw_string (private_data->header_win, HEADER_FONT (widget), gc,
- 3 + (max_month_width - str_width) / 2,
- y, buffer);
+ x = 3 + (max_month_width - logical_rect.width / PANGO_SCALE) / 2;
else
- gdk_draw_string (private_data->header_win, HEADER_FONT (widget), gc,
- 3 + private_data->arrow_width + (max_month_width - str_width)/2,
- y, buffer);
-
- y += CALENDAR_YSEP + HEADER_FONT (widget)->descent;
+ x = 3 + private_data->arrow_width + (max_month_width - logical_rect.width / PANGO_SCALE)/2;
+
+ gdk_draw_layout (private_data->header_win, gc, x, y, layout);
gdk_gc_set_foreground (gc, BACKGROUND_COLOR (GTK_WIDGET (calendar)));
@@ -1603,7 +1601,8 @@ gtk_calendar_paint_header (GtkWidget *widget)
gtk_calendar_paint_arrow (widget, ARROW_MONTH_RIGHT);
gtk_calendar_paint_arrow (widget, ARROW_YEAR_LEFT);
gtk_calendar_paint_arrow (widget, ARROW_YEAR_RIGHT);
-
+
+ pango_layout_unref (layout);
}
static void
@@ -1616,7 +1615,8 @@ gtk_calendar_paint_day_names (GtkWidget *widget)
int day_width, cal_width;
gint cal_height;
int day_wid_sep;
- int str_width;
+ PangoLayout *layout;
+ PangoRectangle logical_rect;
GtkCalendarPrivateData *private_data;
g_return_if_fail (widget != NULL);
@@ -1667,6 +1667,9 @@ gtk_calendar_paint_day_names (GtkWidget *widget)
/*
* Write the labels
*/
+
+ layout = gtk_widget_create_pango_layout (widget);
+
gdk_gc_set_foreground (gc, &widget->style->fg[GTK_STATE_SELECTED]);
for (i = 0; i < 7; i++)
{
@@ -1674,16 +1677,20 @@ gtk_calendar_paint_day_names (GtkWidget *widget)
if (calendar->display_flags & GTK_CALENDAR_WEEK_START_MONDAY)
day= (day+1)%7;
sprintf (buffer, "%s", default_abbreviated_dayname[day]);
- str_width = gdk_string_measure (LABEL_FONT (widget), buffer);
- gdk_draw_string (private_data->day_name_win, LABEL_FONT (widget),
- gc,
+
+ pango_layout_set_text (layout, buffer, -1);
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+
+ gdk_draw_layout (private_data->day_name_win, gc,
((private_data->week_width ? CALENDAR_XSEP : CALENDAR_MARGIN)
+ day_wid_sep * i
+ private_data->week_width
- + (day_width - str_width)/2),
- CALENDAR_MARGIN + DAY_YPAD
- + private_data->max_label_char_ascent, buffer);
+ + (day_width - logical_rect.width / PANGO_SCALE)/2),
+ CALENDAR_MARGIN + DAY_YPAD + private_data->max_label_char_ascent + logical_rect.y / PANGO_SCALE,
+ layout);
}
+
+ pango_layout_unref (layout);
}
static void
@@ -1694,8 +1701,10 @@ gtk_calendar_paint_week_numbers (GtkWidget *widget)
gint row, week = 0, year;
gint x_loc;
char buffer[3];
- gint y_baseline, day_height;
+ gint y_loc, day_height;
GtkCalendarPrivateData *private_data;
+ PangoLayout *layout;
+ PangoRectangle logical_rect;
g_return_if_fail (widget != NULL);
g_return_if_fail (widget->window != NULL);
@@ -1743,6 +1752,8 @@ gtk_calendar_paint_week_numbers (GtkWidget *widget)
* Write the labels
*/
+ layout = gtk_widget_create_pango_layout (widget);
+
gdk_gc_set_foreground (gc, &widget->style->fg[GTK_STATE_SELECTED]);
day_height = row_height (calendar);
for (row = 0; row < 6; row++)
@@ -1750,28 +1761,25 @@ gtk_calendar_paint_week_numbers (GtkWidget *widget)
year = calendar->year;
if (calendar->day[row][6] < 15 && row > 3 && calendar->month == 11)
year++;
- y_baseline = (top_y_for_row (calendar, row)
- + (day_height + LABEL_FONT (widget)->ascent
- - LABEL_FONT (widget)->descent)/2);
+
g_return_if_fail (week_of_year (&week, &year,
((calendar->day[row][6] < 15 && row > 3 ? 1 : 0)
+ calendar->month) % 12 + 1, calendar->day[row][6]));
- x_loc= (private_data->week_width - (private_data->week_width - CALENDAR_XSEP
- - DAY_XPAD * 2 - CALENDAR_MARGIN ) / 2
- - private_data->max_week_char_width
- - CALENDAR_XSEP - DAY_XPAD);
-
- if (week > 9)
- {
- sprintf (buffer, "%d", week/10);
- gdk_draw_string (private_data->week_win, LABEL_FONT (widget), gc,
- x_loc, y_baseline , buffer);
- }
-
- sprintf (buffer, "%d", week%10);
- gdk_draw_string (private_data->week_win, LABEL_FONT (widget), gc,
- x_loc + private_data->max_week_char_width, y_baseline , buffer);
+
+ sprintf (buffer, "%d", week);
+ pango_layout_set_text (layout, buffer, -1);
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+
+ y_loc = top_y_for_row (calendar, row) + (day_height - logical_rect.height / PANGO_SCALE) / 2;
+
+ x_loc = (private_data->week_width
+ - logical_rect.width / PANGO_SCALE
+ - CALENDAR_XSEP - DAY_XPAD);
+
+ gdk_draw_layout (private_data->week_win, gc, x_loc, y_loc, layout);
}
+
+ pango_layout_unref (layout);
}
static void
@@ -1816,9 +1824,11 @@ gtk_calendar_paint_day (GtkWidget *widget,
gint x_left;
gint x_loc;
gint y_top;
- gint y_baseline;
+ gint y_loc;
gint day_xspace;
GtkCalendarPrivateData *private_data;
+ PangoLayout *layout;
+ PangoRectangle logical_rect;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CALENDAR (widget));
@@ -1847,7 +1857,6 @@ gtk_calendar_paint_day (GtkWidget *widget,
x_loc = x_left + private_data->day_width / 2 + private_data->max_day_char_width;
y_top = top_y_for_row (calendar, row);
- y_baseline = y_top + (day_height + private_data->max_day_char_ascent)/2;
gdk_window_clear_area (private_data->main_win, x_left, y_top,
private_data->day_width, day_height);
@@ -1891,17 +1900,21 @@ gtk_calendar_paint_day (GtkWidget *widget,
}
+ layout = gtk_widget_create_pango_layout (widget);
+
sprintf (buffer, "%d", day);
- x_loc -= gdk_string_measure (DAY_FONT (widget), buffer);
- sprintf (buffer, "%d", day);
- gdk_draw_string (private_data->main_win,
- DAY_FONT (widget), gc,
- x_loc, y_baseline, buffer);
+ pango_layout_set_text (layout, buffer, -1);
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+
+ x_loc -= logical_rect.width / PANGO_SCALE;
+
+ y_loc = y_top + (day_height - logical_rect.height / PANGO_SCALE) / 2;
+ gdk_draw_layout (private_data->main_win, gc,
+ x_loc, y_loc, layout);
if (calendar->marked_date[day-1]
&& calendar->day_month[row][col] == MONTH_CURRENT)
- gdk_draw_string (private_data->main_win,
- DAY_FONT (widget), gc,
- x_loc-1, y_baseline, buffer);
+ gdk_draw_layout (private_data->main_win, gc,
+ x_loc-1, y_loc, layout);
if (GTK_WIDGET_HAS_FOCUS (calendar)
&& calendar->focus_row == row && calendar->focus_col == col)
@@ -1911,6 +1924,8 @@ gtk_calendar_paint_day (GtkWidget *widget,
private_data->day_width-1, day_height-1);
}
+ pango_layout_unref (layout);
+
}
diff --git a/gtk/gtkcheckbutton.c b/gtk/gtkcheckbutton.c
index c9d88bc5f..67567d00a 100644
--- a/gtk/gtkcheckbutton.c
+++ b/gtk/gtkcheckbutton.c
@@ -277,6 +277,10 @@ gtk_check_button_size_allocate (GtkWidget *widget,
GTK_CONTAINER (widget)->border_width - 1);
child_allocation.height = MAX (1, allocation->height - (GTK_CONTAINER (widget)->border_width + 1) * 2);
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+ child_allocation.x = allocation->x + allocation->width
+ - (child_allocation.x - allocation->x + child_allocation.width);
+
gtk_widget_size_allocate (GTK_BIN (button)->child, &child_allocation);
}
}
@@ -401,9 +405,12 @@ gtk_real_check_button_draw_indicator (GtkCheckButton *check_button,
state_type = GTK_WIDGET_STATE (widget);
}
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+ x = widget->allocation.x + widget->allocation.width - (width + x - widget->allocation.x);
+
gtk_paint_check (widget->style, window,
state_type, shadow_type,
area, widget, "checkbutton",
- x + 1, y + 1, width, height);
+ x, y, width, height);
}
}
diff --git a/gtk/gtkclist.c b/gtk/gtkclist.c
index c0ddcb62f..84e0b9395 100644
--- a/gtk/gtkclist.c
+++ b/gtk/gtkclist.c
@@ -78,17 +78,17 @@ COLUMN_FROM_XPIXEL (GtkCList * clist,
gint x)
{
gint i, cx;
-
+
for (i = 0; i < clist->columns; i++)
if (clist->column[i].visible)
{
cx = clist->column[i].area.x + clist->hoffset;
-
+
if (x >= (cx - (COLUMN_INSET + CELL_SPACING)) &&
x <= (cx + clist->column[i].area.width + COLUMN_INSET))
return i;
}
-
+
/* no match */
return -1;
}
@@ -111,10 +111,10 @@ static inline gint
LIST_WIDTH (GtkCList * clist)
{
gint last_column;
-
+
for (last_column = clist->columns - 1;
last_column >= 0 && !clist->column[last_column].visible; last_column--);
-
+
if (last_column >= 0)
return (clist->column[last_column].area.x +
clist->column[last_column].area.width +
@@ -397,7 +397,7 @@ static void draw_drag_highlight (GtkCList *clist,
GtkCListRow *dest_row,
gint dest_row_number,
GtkCListDragPos drag_pos);
-
+
/* Size Allocation / Requisition */
static void size_allocate_title_buttons (GtkCList *clist);
static void size_allocate_columns (GtkCList *clist,
@@ -464,7 +464,7 @@ GtkType
gtk_clist_get_type (void)
{
static GtkType clist_type = 0;
-
+
if (!clist_type)
{
static const GtkTypeInfo clist_info =
@@ -478,10 +478,10 @@ gtk_clist_get_type (void)
/* reserved_2 */ NULL,
(GtkClassInitFunc) NULL,
};
-
+
clist_type = gtk_type_unique (GTK_TYPE_CONTAINER, &clist_info);
}
-
+
return clist_type;
}
@@ -493,13 +493,13 @@ gtk_clist_class_init (GtkCListClass *klass)
GtkWidgetClass *widget_class;
GtkContainerClass *container_class;
GtkBindingSet *binding_set;
-
+
object_class = (GtkObjectClass *) klass;
widget_class = (GtkWidgetClass *) klass;
container_class = (GtkContainerClass *) klass;
-
+
parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
-
+
gobject_class->finalize = gtk_clist_finalize;
object_class->set_arg = gtk_clist_set_arg;
@@ -538,7 +538,7 @@ gtk_clist_class_init (GtkCListClass *klass)
GTK_TYPE_SORT_TYPE,
GTK_ARG_READWRITE,
ARG_SORT_TYPE);
-
+
widget_class->set_scroll_adjustments_signal =
gtk_signal_new ("set_scroll_adjustments",
GTK_RUN_LAST,
@@ -546,7 +546,7 @@ gtk_clist_class_init (GtkCListClass *klass)
GTK_SIGNAL_OFFSET (GtkCListClass, set_scroll_adjustments),
gtk_marshal_NONE__POINTER_POINTER,
GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
-
+
clist_signals[SELECT_ROW] =
gtk_signal_new ("select_row",
GTK_RUN_FIRST,
@@ -586,7 +586,7 @@ gtk_clist_class_init (GtkCListClass *klass)
GTK_SIGNAL_OFFSET (GtkCListClass, resize_column),
gtk_marshal_NONE__INT_INT,
GTK_TYPE_NONE, 2, GTK_TYPE_INT, GTK_TYPE_INT);
-
+
clist_signals[TOGGLE_FOCUS_ROW] =
gtk_signal_new ("toggle_focus_row",
GTK_RUN_LAST | GTK_RUN_ACTION,
@@ -666,7 +666,7 @@ gtk_clist_class_init (GtkCListClass *klass)
gtk_marshal_NONE__NONE,
GTK_TYPE_NONE, 0);
gtk_object_class_add_signals (object_class, clist_signals, LAST_SIGNAL);
-
+
widget_class->realize = gtk_clist_realize;
widget_class->unrealize = gtk_clist_unrealize;
widget_class->map = gtk_clist_map;
@@ -690,14 +690,14 @@ gtk_clist_class_init (GtkCListClass *klass)
widget_class->drag_drop = gtk_clist_drag_drop;
widget_class->drag_data_get = gtk_clist_drag_data_get;
widget_class->drag_data_received = gtk_clist_drag_data_received;
-
+
/* container_class->add = NULL; use the default GtkContainerClass warning */
/* container_class->remove=NULL; use the default GtkContainerClass warning */
-
+
container_class->forall = gtk_clist_forall;
container_class->focus = gtk_clist_focus;
container_class->set_focus_child = gtk_clist_set_focus_child;
-
+
klass->set_scroll_adjustments = gtk_clist_set_scroll_adjustments;
klass->refresh = clist_refresh;
klass->select_row = real_select_row;
@@ -727,7 +727,7 @@ gtk_clist_class_init (GtkCListClass *klass)
klass->abort_column_resize = abort_column_resize;
klass->set_cell_contents = set_cell_contents;
klass->cell_size_request = cell_size_request;
-
+
binding_set = gtk_binding_set_by_class (klass);
gtk_binding_entry_add_signal (binding_set, GDK_Up, 0,
"scroll_vertical", 2,
@@ -753,7 +753,7 @@ gtk_clist_class_init (GtkCListClass *klass)
"scroll_vertical", 2,
GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
GTK_TYPE_FLOAT, 1.0);
-
+
gtk_binding_entry_add_signal (binding_set, GDK_Up, GDK_SHIFT_MASK,
"extend_selection", 3,
GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
@@ -780,7 +780,7 @@ gtk_clist_class_init (GtkCListClass *klass)
"extend_selection", 3,
GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
GTK_TYPE_FLOAT, 1.0, GTK_TYPE_BOOL, TRUE);
-
+
gtk_binding_entry_add_signal (binding_set, GDK_Left, 0,
"scroll_horizontal", 2,
GTK_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
@@ -797,7 +797,7 @@ gtk_clist_class_init (GtkCListClass *klass)
"scroll_horizontal", 2,
GTK_TYPE_ENUM, GTK_SCROLL_JUMP,
GTK_TYPE_FLOAT, 1.0);
-
+
gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
"undo_selection", 0);
gtk_binding_entry_add_signal (binding_set, GDK_Escape, 0,
@@ -832,9 +832,9 @@ gtk_clist_set_arg (GtkObject *object,
guint arg_id)
{
GtkCList *clist;
-
+
clist = GTK_CLIST (object);
-
+
switch (arg_id)
{
case ARG_N_COLUMNS: /* construct-only arg, only set when !GTK_CONSTRUCTED */
@@ -873,13 +873,13 @@ gtk_clist_get_arg (GtkObject *object,
guint arg_id)
{
GtkCList *clist;
-
+
clist = GTK_CLIST (object);
-
+
switch (arg_id)
{
guint i;
-
+
case ARG_N_COLUMNS:
GTK_VALUE_UINT (*arg) = clist->columns;
break;
@@ -921,73 +921,72 @@ static void
gtk_clist_init (GtkCList *clist)
{
clist->flags = 0;
-
+
GTK_WIDGET_UNSET_FLAGS (clist, GTK_NO_WINDOW);
GTK_WIDGET_SET_FLAGS (clist, GTK_CAN_FOCUS);
GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
GTK_CLIST_SET_FLAG (clist, CLIST_DRAW_DRAG_LINE);
GTK_CLIST_SET_FLAG (clist, CLIST_USE_DRAG_ICONS);
-
+
clist->row_mem_chunk = NULL;
clist->cell_mem_chunk = NULL;
-
+
clist->freeze_count = 0;
-
+
clist->rows = 0;
- clist->row_center_offset = 0;
clist->row_height = 0;
clist->row_list = NULL;
clist->row_list_end = NULL;
-
+
clist->columns = 0;
-
+
clist->title_window = NULL;
clist->column_title_area.x = 0;
clist->column_title_area.y = 0;
clist->column_title_area.width = 1;
clist->column_title_area.height = 1;
-
+
clist->clist_window = NULL;
clist->clist_window_width = 1;
clist->clist_window_height = 1;
-
+
clist->hoffset = 0;
clist->voffset = 0;
-
+
clist->shadow_type = GTK_SHADOW_IN;
clist->vadjustment = NULL;
clist->hadjustment = NULL;
-
+
clist->button_actions[0] = GTK_BUTTON_SELECTS | GTK_BUTTON_DRAGS;
clist->button_actions[1] = GTK_BUTTON_IGNORED;
clist->button_actions[2] = GTK_BUTTON_IGNORED;
clist->button_actions[3] = GTK_BUTTON_IGNORED;
clist->button_actions[4] = GTK_BUTTON_IGNORED;
-
+
clist->cursor_drag = NULL;
clist->xor_gc = NULL;
clist->fg_gc = NULL;
clist->bg_gc = NULL;
clist->x_drag = 0;
-
+
clist->selection_mode = GTK_SELECTION_SINGLE;
clist->selection = NULL;
clist->selection_end = NULL;
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
-
+
clist->focus_row = -1;
clist->undo_anchor = -1;
-
+
clist->anchor = -1;
clist->anchor_state = GTK_STATE_SELECTED;
clist->drag_pos = -1;
clist->htimer = 0;
clist->vtimer = 0;
-
+
clist->click_cell.row = -1;
clist->click_cell.column = -1;
-
+
clist->compare = default_compare;
clist->sort_type = GTK_SORT_ASCENDING;
clist->sort_column = 0;
@@ -1003,10 +1002,10 @@ gtk_clist_construct (GtkCList *clist,
g_return_if_fail (GTK_IS_CLIST (clist));
g_return_if_fail (columns > 0);
g_return_if_fail (GTK_OBJECT_CONSTRUCTED (clist) == FALSE);
-
+
/* mark the object as constructed */
gtk_object_constructed (GTK_OBJECT (clist));
-
+
/* initalize memory chunks, if this has not been done by any
* possibly derived widget
*/
@@ -1016,23 +1015,23 @@ gtk_clist_construct (GtkCList *clist,
sizeof (GtkCListRow) *
CLIST_OPTIMUM_SIZE,
G_ALLOC_AND_FREE);
-
+
if (!clist->cell_mem_chunk)
clist->cell_mem_chunk = g_mem_chunk_new ("clist cell mem chunk",
sizeof (GtkCell) * columns,
sizeof (GtkCell) * columns *
CLIST_OPTIMUM_SIZE,
G_ALLOC_AND_FREE);
-
+
/* set number of columns, allocate memory */
clist->columns = columns;
clist->column = columns_new (clist);
-
+
/* there needs to be at least one column button
* because there is alot of code that will break if it
* isn't there*/
column_button_create (clist, 0);
-
+
if (titles)
{
guint i;
@@ -1064,16 +1063,16 @@ gtk_clist_new (gint columns)
{
return gtk_clist_new_with_titles (columns, NULL);
}
-
+
GtkWidget*
gtk_clist_new_with_titles (gint columns,
gchar *titles[])
{
GtkWidget *widget;
-
+
widget = gtk_type_new (GTK_TYPE_CLIST);
gtk_clist_construct (GTK_CLIST (widget), columns, titles);
-
+
return widget;
}
@@ -1082,7 +1081,7 @@ gtk_clist_set_hadjustment (GtkCList *clist,
GtkAdjustment *adjustment)
{
GtkAdjustment *old_adjustment;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
if (adjustment)
@@ -1092,20 +1091,20 @@ gtk_clist_set_hadjustment (GtkCList *clist,
return;
old_adjustment = clist->hadjustment;
-
+
if (clist->hadjustment)
{
gtk_signal_disconnect_by_data (GTK_OBJECT (clist->hadjustment), clist);
gtk_object_unref (GTK_OBJECT (clist->hadjustment));
}
-
+
clist->hadjustment = adjustment;
-
+
if (clist->hadjustment)
{
gtk_object_ref (GTK_OBJECT (clist->hadjustment));
gtk_object_sink (GTK_OBJECT (clist->hadjustment));
-
+
gtk_signal_connect (GTK_OBJECT (clist->hadjustment), "changed",
(GtkSignalFunc) hadjustment_changed,
(gpointer) clist);
@@ -1113,7 +1112,7 @@ gtk_clist_set_hadjustment (GtkCList *clist,
(GtkSignalFunc) hadjustment_value_changed,
(gpointer) clist);
}
-
+
if (!clist->hadjustment || !old_adjustment)
gtk_widget_queue_resize (GTK_WIDGET (clist));
}
@@ -1123,7 +1122,7 @@ gtk_clist_get_hadjustment (GtkCList *clist)
{
g_return_val_if_fail (clist != NULL, NULL);
g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
-
+
return clist->hadjustment;
}
@@ -1132,30 +1131,30 @@ gtk_clist_set_vadjustment (GtkCList *clist,
GtkAdjustment *adjustment)
{
GtkAdjustment *old_adjustment;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
if (adjustment)
g_return_if_fail (GTK_IS_ADJUSTMENT (adjustment));
-
+
if (clist->vadjustment == adjustment)
return;
old_adjustment = clist->vadjustment;
-
+
if (clist->vadjustment)
{
gtk_signal_disconnect_by_data (GTK_OBJECT (clist->vadjustment), clist);
gtk_object_unref (GTK_OBJECT (clist->vadjustment));
}
-
+
clist->vadjustment = adjustment;
-
+
if (clist->vadjustment)
{
gtk_object_ref (GTK_OBJECT (clist->vadjustment));
gtk_object_sink (GTK_OBJECT (clist->vadjustment));
-
+
gtk_signal_connect (GTK_OBJECT (clist->vadjustment), "changed",
(GtkSignalFunc) vadjustment_changed,
(gpointer) clist);
@@ -1163,7 +1162,7 @@ gtk_clist_set_vadjustment (GtkCList *clist,
(GtkSignalFunc) vadjustment_value_changed,
(gpointer) clist);
}
-
+
if (!clist->vadjustment || !old_adjustment)
gtk_widget_queue_resize (GTK_WIDGET (clist));
}
@@ -1173,7 +1172,7 @@ gtk_clist_get_vadjustment (GtkCList *clist)
{
g_return_val_if_fail (clist != NULL, NULL);
g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
-
+
return clist->vadjustment;
}
@@ -1194,9 +1193,9 @@ gtk_clist_set_shadow_type (GtkCList *clist,
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
clist->shadow_type = type;
-
+
if (GTK_WIDGET_VISIBLE (clist))
gtk_widget_queue_resize (GTK_WIDGET (clist));
}
@@ -1207,21 +1206,21 @@ gtk_clist_set_selection_mode (GtkCList *clist,
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (mode == clist->selection_mode)
return;
-
+
clist->selection_mode = mode;
clist->anchor = -1;
clist->anchor_state = GTK_STATE_SELECTED;
clist->drag_pos = -1;
clist->undo_anchor = clist->focus_row;
-
+
g_list_free (clist->undo_selection);
g_list_free (clist->undo_unselection);
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
-
+
switch (mode)
{
case GTK_SELECTION_MULTIPLE:
@@ -1239,7 +1238,7 @@ gtk_clist_freeze (GtkCList *clist)
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
clist->freeze_count++;
}
@@ -1248,7 +1247,7 @@ gtk_clist_thaw (GtkCList *clist)
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (clist->freeze_count)
{
clist->freeze_count--;
@@ -1280,7 +1279,7 @@ gtk_clist_column_titles_show (GtkCList *clist)
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (!GTK_CLIST_SHOW_TITLES(clist))
{
GTK_CLIST_SET_FLAG (clist, CLIST_SHOW_TITLES);
@@ -1295,7 +1294,7 @@ gtk_clist_column_titles_hide (GtkCList *clist)
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (GTK_CLIST_SHOW_TITLES(clist))
{
GTK_CLIST_UNSET_FLAG (clist, CLIST_SHOW_TITLES);
@@ -1311,18 +1310,18 @@ gtk_clist_column_title_active (GtkCList *clist,
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (column < 0 || column >= clist->columns)
return;
if (!clist->column[column].button || !clist->column[column].button_passive)
return;
-
+
clist->column[column].button_passive = FALSE;
-
+
gtk_signal_disconnect_by_func (GTK_OBJECT (clist->column[column].button),
(GtkSignalFunc) column_title_passive_func,
NULL);
-
+
GTK_WIDGET_SET_FLAGS (clist->column[column].button, GTK_CAN_FOCUS);
if (GTK_WIDGET_VISIBLE (clist))
gtk_widget_queue_draw (clist->column[column].button);
@@ -1333,27 +1332,27 @@ gtk_clist_column_title_passive (GtkCList *clist,
gint column)
{
GtkButton *button;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (column < 0 || column >= clist->columns)
return;
if (!clist->column[column].button || clist->column[column].button_passive)
return;
-
+
button = GTK_BUTTON (clist->column[column].button);
-
+
clist->column[column].button_passive = TRUE;
-
+
if (button->button_down)
gtk_button_released (button);
if (button->in_button)
gtk_button_leave (button);
-
+
gtk_signal_connect (GTK_OBJECT (clist->column[column].button), "event",
(GtkSignalFunc) column_title_passive_func, NULL);
-
+
GTK_WIDGET_UNSET_FLAGS (clist->column[column].button, GTK_CAN_FOCUS);
if (GTK_WIDGET_VISIBLE (clist))
gtk_widget_queue_draw (clist->column[column].button);
@@ -1363,10 +1362,10 @@ void
gtk_clist_column_titles_active (GtkCList *clist)
{
gint i;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
for (i = 0; i < clist->columns; i++)
gtk_clist_column_title_active (clist, i);
}
@@ -1375,10 +1374,10 @@ void
gtk_clist_column_titles_passive (GtkCList *clist)
{
gint i;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
for (i = 0; i < clist->columns; i++)
gtk_clist_column_title_passive (clist, i);
}
@@ -1392,13 +1391,13 @@ gtk_clist_set_column_title (GtkCList *clist,
GtkWidget *old_widget;
GtkWidget *alignment = NULL;
GtkWidget *label;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (column < 0 || column >= clist->columns)
return;
-
+
/* if the column button doesn't currently exist,
* it has to be created first */
if (!clist->column[column].button)
@@ -1406,34 +1405,34 @@ gtk_clist_set_column_title (GtkCList *clist,
column_button_create (clist, column);
new_button = 1;
}
-
+
column_title_new (clist, column, title);
-
+
/* remove and destroy the old widget */
old_widget = GTK_BIN (clist->column[column].button)->child;
if (old_widget)
gtk_container_remove (GTK_CONTAINER (clist->column[column].button), old_widget);
-
+
/* create new alignment based no column justification */
switch (clist->column[column].justification)
{
case GTK_JUSTIFY_LEFT:
alignment = gtk_alignment_new (0.0, 0.5, 0.0, 0.0);
break;
-
+
case GTK_JUSTIFY_RIGHT:
alignment = gtk_alignment_new (1.0, 0.5, 0.0, 0.0);
break;
-
+
case GTK_JUSTIFY_CENTER:
alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
break;
-
+
case GTK_JUSTIFY_FILL:
alignment = gtk_alignment_new (0.5, 0.5, 0.0, 0.0);
break;
}
-
+
gtk_widget_push_composite_child ();
label = gtk_label_new (clist->column[column].title);
gtk_widget_pop_composite_child ();
@@ -1441,7 +1440,7 @@ gtk_clist_set_column_title (GtkCList *clist,
gtk_container_add (GTK_CONTAINER (clist->column[column].button), alignment);
gtk_widget_show (label);
gtk_widget_show (alignment);
-
+
/* if this button didn't previously exist, then the
* column button positions have to be re-computed */
if (GTK_WIDGET_VISIBLE (clist) && new_button)
@@ -1454,10 +1453,10 @@ gtk_clist_get_column_title (GtkCList *clist,
{
g_return_val_if_fail (clist != NULL, NULL);
g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
-
+
if (column < 0 || column >= clist->columns)
return NULL;
-
+
return clist->column[column].title;
}
@@ -1468,13 +1467,13 @@ gtk_clist_set_column_widget (GtkCList *clist,
{
gint new_button = 0;
GtkWidget *old_widget;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (column < 0 || column >= clist->columns)
return;
-
+
/* if the column button doesn't currently exist,
* it has to be created first */
if (!clist->column[column].button)
@@ -1482,22 +1481,22 @@ gtk_clist_set_column_widget (GtkCList *clist,
column_button_create (clist, column);
new_button = 1;
}
-
+
column_title_new (clist, column, NULL);
-
+
/* remove and destroy the old widget */
old_widget = GTK_BIN (clist->column[column].button)->child;
if (old_widget)
gtk_container_remove (GTK_CONTAINER (clist->column[column].button),
old_widget);
-
+
/* add and show the widget */
if (widget)
{
gtk_container_add (GTK_CONTAINER (clist->column[column].button), widget);
gtk_widget_show (widget);
}
-
+
/* if this button didn't previously exist, then the
* column button positions have to be re-computed */
if (GTK_WIDGET_VISIBLE (clist) && new_button)
@@ -1510,13 +1509,13 @@ gtk_clist_get_column_widget (GtkCList *clist,
{
g_return_val_if_fail (clist != NULL, NULL);
g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
-
+
if (column < 0 || column >= clist->columns)
return NULL;
-
+
if (clist->column[column].button)
return GTK_BUTTON (clist->column[column].button)->child;
-
+
return NULL;
}
@@ -1526,44 +1525,44 @@ gtk_clist_set_column_justification (GtkCList *clist,
GtkJustification justification)
{
GtkWidget *alignment;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (column < 0 || column >= clist->columns)
return;
-
+
clist->column[column].justification = justification;
-
+
/* change the alinment of the button title if it's not a
* custom widget */
if (clist->column[column].title)
{
alignment = GTK_BIN (clist->column[column].button)->child;
-
+
switch (clist->column[column].justification)
{
case GTK_JUSTIFY_LEFT:
gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.0, 0.5, 0.0, 0.0);
break;
-
+
case GTK_JUSTIFY_RIGHT:
gtk_alignment_set (GTK_ALIGNMENT (alignment), 1.0, 0.5, 0.0, 0.0);
break;
-
+
case GTK_JUSTIFY_CENTER:
gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
break;
-
+
case GTK_JUSTIFY_FILL:
gtk_alignment_set (GTK_ALIGNMENT (alignment), 0.5, 0.5, 0.0, 0.0);
break;
-
+
default:
break;
}
}
-
+
if (CLIST_UNFROZEN (clist))
draw_rows (clist, NULL);
}
@@ -1575,28 +1574,28 @@ gtk_clist_set_column_visibility (GtkCList *clist,
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (column < 0 || column >= clist->columns)
return;
if (clist->column[column].visible == visible)
return;
-
+
/* don't hide last visible column */
if (!visible)
{
gint i;
gint vis_columns = 0;
-
+
for (i = 0, vis_columns = 0; i < clist->columns && vis_columns < 2; i++)
if (clist->column[i].visible)
vis_columns++;
-
+
if (vis_columns < 2)
return;
}
-
+
clist->column[column].visible = visible;
-
+
if (clist->column[column].button)
{
if (visible)
@@ -1615,16 +1614,16 @@ gtk_clist_set_column_resizeable (GtkCList *clist,
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (column < 0 || column >= clist->columns)
return;
if (clist->column[column].resizeable == resizeable)
return;
-
+
clist->column[column].resizeable = resizeable;
if (resizeable)
clist->column[column].auto_resize = FALSE;
-
+
if (GTK_WIDGET_VISIBLE (clist))
size_allocate_title_buttons (clist);
}
@@ -1636,12 +1635,12 @@ gtk_clist_set_column_auto_resize (GtkCList *clist,
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (column < 0 || column >= clist->columns)
return;
if (clist->column[column].auto_resize == auto_resize)
return;
-
+
clist->column[column].auto_resize = auto_resize;
if (auto_resize)
{
@@ -1649,12 +1648,12 @@ gtk_clist_set_column_auto_resize (GtkCList *clist,
if (!GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
{
gint width;
-
+
width = gtk_clist_optimal_column_width (clist, column);
gtk_clist_set_column_width (clist, column, width);
}
}
-
+
if (GTK_WIDGET_VISIBLE (clist))
size_allocate_title_buttons (clist);
}
@@ -1664,20 +1663,20 @@ gtk_clist_columns_autosize (GtkCList *clist)
{
gint i;
gint width;
-
+
g_return_val_if_fail (clist != NULL, 0);
g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
-
+
gtk_clist_freeze (clist);
width = 0;
for (i = 0; i < clist->columns; i++)
{
gtk_clist_set_column_width (clist, i,
gtk_clist_optimal_column_width (clist, i));
-
+
width += clist->column[i].width;
}
-
+
gtk_clist_thaw (clist);
return width;
}
@@ -1689,66 +1688,66 @@ gtk_clist_optimal_column_width (GtkCList *clist,
GtkRequisition requisition;
GList *list;
gint width;
-
+
g_return_val_if_fail (clist != NULL, 0);
g_return_val_if_fail (GTK_CLIST (clist), 0);
-
+
if (column < 0 || column > clist->columns)
return 0;
-
+
if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[column].button)
width = (clist->column[column].button->requisition.width)
#if 0
- (CELL_SPACING + (2 * COLUMN_INSET)))
+ (CELL_SPACING + (2 * COLUMN_INSET)))
#endif
- ;
- else
- width = 0;
+ ;
+ else
+ width = 0;
-for (list = clist->row_list; list; list = list->next)
-{
+ for (list = clist->row_list; list; list = list->next)
+ {
GTK_CLIST_GET_CLASS (clist)->cell_size_request
- (clist, GTK_CLIST_ROW (list), column, &requisition);
- width = MAX (width, requisition.width);
-}
+ (clist, GTK_CLIST_ROW (list), column, &requisition);
+ width = MAX (width, requisition.width);
+ }
-return width;
+ return width;
}
void
gtk_clist_set_column_width (GtkCList *clist,
- gint column,
- gint width)
+ gint column,
+ gint width)
{
-g_return_if_fail (clist != NULL);
-g_return_if_fail (GTK_IS_CLIST (clist));
+ g_return_if_fail (clist != NULL);
+ g_return_if_fail (GTK_IS_CLIST (clist));
-if (column < 0 || column >= clist->columns)
- return;
-
- gtk_signal_emit (GTK_OBJECT (clist), clist_signals[RESIZE_COLUMN],
- column, width);
- }
+ if (column < 0 || column >= clist->columns)
+ return;
+
+ gtk_signal_emit (GTK_OBJECT (clist), clist_signals[RESIZE_COLUMN],
+ column, width);
+}
void
gtk_clist_set_column_min_width (GtkCList *clist,
- gint column,
- gint min_width)
+ gint column,
+ gint min_width)
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (column < 0 || column >= clist->columns)
return;
if (clist->column[column].min_width == min_width)
return;
-
+
if (clist->column[column].max_width >= 0 &&
clist->column[column].max_width < min_width)
clist->column[column].min_width = clist->column[column].max_width;
else
clist->column[column].min_width = min_width;
-
+
if (clist->column[column].area.width < clist->column[column].min_width)
gtk_clist_set_column_width (clist, column,clist->column[column].min_width);
}
@@ -1760,12 +1759,12 @@ gtk_clist_set_column_max_width (GtkCList *clist,
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (column < 0 || column >= clist->columns)
return;
if (clist->column[column].max_width == max_width)
return;
-
+
if (clist->column[column].min_width >= 0 && max_width >= 0 &&
clist->column[column].min_width > max_width)
clist->column[column].max_width = clist->column[column].min_width;
@@ -1796,17 +1795,17 @@ column_auto_resize (GtkCList *clist,
{
/* resize column if needed for auto_resize */
GtkRequisition requisition;
-
+
if (!clist->column[column].auto_resize ||
GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
return;
-
+
if (clist_row)
GTK_CLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
- column, &requisition);
+ column, &requisition);
else
requisition.width = 0;
-
+
if (requisition.width > clist->column[column].width)
gtk_clist_set_column_width (clist, column, requisition.width);
else if (requisition.width < old_width &&
@@ -1814,7 +1813,7 @@ column_auto_resize (GtkCList *clist,
{
GList *list;
gint new_width = 0;
-
+
/* run a "gtk_clist_optimal_column_width" but break, if
* the column doesn't shrink */
if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[column].button)
@@ -1822,7 +1821,7 @@ column_auto_resize (GtkCList *clist,
(CELL_SPACING + (2 * COLUMN_INSET)));
else
new_width = 0;
-
+
for (list = clist->row_list; list; list = list->next)
{
GTK_CLIST_GET_CLASS (clist)->cell_size_request
@@ -1844,7 +1843,7 @@ real_resize_column (GtkCList *clist,
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (column < 0 || column >= clist->columns)
return;
@@ -1853,17 +1852,17 @@ real_resize_column (GtkCList *clist,
if (clist->column[column].max_width >= 0 &&
width > clist->column[column].max_width)
width = clist->column[column].max_width;
-
+
clist->column[column].width = width;
clist->column[column].width_set = TRUE;
-
+
/* FIXME: this is quite expensive to do if the widget hasn't
* been size_allocated yet, and pointless. Should
* a flag be kept
*/
size_allocate_columns (clist, TRUE);
size_allocate_title_buttons (clist);
-
+
CLIST_REFRESH (clist);
}
@@ -1872,18 +1871,18 @@ abort_column_resize (GtkCList *clist)
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (!GTK_CLIST_IN_DRAG(clist))
return;
-
+
GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
gtk_grab_remove (GTK_WIDGET (clist));
gdk_pointer_ungrab (GDK_CURRENT_TIME);
clist->drag_pos = -1;
-
+
if (clist->x_drag >= 0 && clist->x_drag <= clist->clist_window_width - 1)
draw_xor_line (clist);
-
+
if (GTK_CLIST_ADD_MODE(clist))
{
gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_ON_OFF_DASH, 0,0);
@@ -1898,20 +1897,20 @@ size_allocate_title_buttons (GtkCList *clist)
gint last_column;
gint last_button = 0;
gint i;
-
+
if (!GTK_WIDGET_REALIZED (clist))
return;
-
+
button_allocation.x = clist->hoffset;
button_allocation.y = 0;
button_allocation.width = 0;
button_allocation.height = clist->column_title_area.height;
-
+
/* find last visible column */
for (last_column = clist->columns - 1; last_column >= 0; last_column--)
if (clist->column[last_column].visible)
break;
-
+
for (i = 0; i < last_column; i++)
{
if (!clist->column[i].visible)
@@ -1920,21 +1919,21 @@ size_allocate_title_buttons (GtkCList *clist)
gdk_window_hide (clist->column[i].window);
continue;
}
-
+
button_allocation.width += (clist->column[i].area.width +
CELL_SPACING + 2 * COLUMN_INSET);
-
+
if (!clist->column[i + 1].button)
{
gdk_window_hide (clist->column[i].window);
continue;
}
-
+
gtk_widget_size_allocate (clist->column[last_button].button,
&button_allocation);
button_allocation.x += button_allocation.width;
button_allocation.width = 0;
-
+
if (clist->column[last_button].resizeable)
{
gdk_window_show (clist->column[last_button].window);
@@ -1945,19 +1944,19 @@ size_allocate_title_buttons (GtkCList *clist)
}
else
gdk_window_hide (clist->column[last_button].window);
-
+
last_button = i + 1;
}
-
+
button_allocation.width += (clist->column[last_column].area.width +
2 * (CELL_SPACING + COLUMN_INSET));
gtk_widget_size_allocate (clist->column[last_button].button,
&button_allocation);
-
+
if (clist->column[last_button].resizeable)
{
button_allocation.x += button_allocation.width;
-
+
gdk_window_show (clist->column[last_button].window);
gdk_window_move_resize (clist->column[last_button].window,
button_allocation.x - (DRAG_WIDTH / 2),
@@ -1974,14 +1973,14 @@ size_allocate_columns (GtkCList *clist,
gint xoffset = CELL_SPACING + COLUMN_INSET;
gint last_column;
gint i;
-
+
/* find last visible column and calculate correct column width */
for (last_column = clist->columns - 1;
last_column >= 0 && !clist->column[last_column].visible; last_column--);
-
+
if (last_column < 0)
return;
-
+
for (i = 0; i <= last_column; i++)
{
if (!clist->column[i].visible)
@@ -1993,14 +1992,14 @@ size_allocate_columns (GtkCList *clist,
clist->column[i].auto_resize && clist->column[i].button)
{
gint width;
-
+
width = (clist->column[i].button->requisition.width -
(CELL_SPACING + (2 * COLUMN_INSET)));
-
+
if (width > clist->column[i].width)
gtk_clist_set_column_width (clist, i, width);
}
-
+
clist->column[i].area.width = clist->column[i].width;
xoffset += clist->column[i].width + CELL_SPACING + (2* COLUMN_INSET);
}
@@ -2012,7 +2011,7 @@ size_allocate_columns (GtkCList *clist,
xoffset += clist->column[i].button->requisition.width;
}
}
-
+
clist->column[last_column].area.width = clist->column[last_column].area.width
+ MAX (0, clist->clist_window_width + COLUMN_INSET - xoffset);
}
@@ -2022,18 +2021,18 @@ list_requisition_width (GtkCList *clist)
{
gint width = CELL_SPACING;
gint i;
-
+
for (i = clist->columns - 1; i >= 0; i--)
{
if (!clist->column[i].visible)
continue;
-
+
if (clist->column[i].width_set)
width += clist->column[i].width + CELL_SPACING + (2 * COLUMN_INSET);
else if (GTK_CLIST_SHOW_TITLES(clist) && clist->column[i].button)
width += clist->column[i].button->requisition.width;
}
-
+
return width;
}
@@ -2050,20 +2049,20 @@ new_column_width (GtkCList *clist,
gint cx;
gint dx;
gint last_column;
-
+
/* first translate the x position from widget->window
* to clist->clist_window */
cx = *x - xthickness;
-
+
for (last_column = clist->columns - 1;
last_column >= 0 && !clist->column[last_column].visible; last_column--);
-
+
/* calculate new column width making sure it doesn't end up
* less than the minimum width */
dx = (COLUMN_LEFT_XPIXEL (clist, column) + COLUMN_INSET +
(column < last_column) * CELL_SPACING);
width = cx - dx;
-
+
if (width < MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width))
{
width = MAX (COLUMN_MIN_WIDTH, clist->column[column].min_width);
@@ -2077,10 +2076,10 @@ new_column_width (GtkCList *clist,
cx = dx + clist->column[column].max_width;
*x = cx + xthickness;
}
-
+
if (cx < 0 || cx > clist->clist_window_width)
*x = -1;
-
+
return width;
}
@@ -2089,16 +2088,16 @@ column_button_create (GtkCList *clist,
gint column)
{
GtkWidget *button;
-
+
gtk_widget_push_composite_child ();
button = clist->column[column].button = gtk_button_new ();
gtk_widget_pop_composite_child ();
-
+
if (GTK_WIDGET_REALIZED (clist) && clist->title_window)
gtk_widget_set_parent_window (clist->column[column].button,
clist->title_window);
gtk_widget_set_parent (button, GTK_WIDGET (clist));
-
+
gtk_signal_connect (GTK_OBJECT (button), "clicked",
(GtkSignalFunc) column_button_clicked,
(gpointer) clist);
@@ -2111,17 +2110,17 @@ column_button_clicked (GtkWidget *widget,
{
gint i;
GtkCList *clist;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CLIST (data));
-
+
clist = GTK_CLIST (data);
-
+
/* find the column who's button was pressed */
for (i = 0; i < clist->columns; i++)
if (clist->column[i].button == widget)
break;
-
+
gtk_signal_emit (GTK_OBJECT (clist), clist_signals[CLICK_COLUMN], i);
}
@@ -2165,17 +2164,17 @@ gtk_clist_get_cell_type (GtkCList *clist,
gint column)
{
GtkCListRow *clist_row;
-
+
g_return_val_if_fail (clist != NULL, -1);
g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
-
+
if (row < 0 || row >= clist->rows)
return -1;
if (column < 0 || column >= clist->columns)
return -1;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
return clist_row->cell[column].type;
}
@@ -2186,21 +2185,21 @@ gtk_clist_set_text (GtkCList *clist,
const gchar *text)
{
GtkCListRow *clist_row;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (row < 0 || row >= clist->rows)
return;
if (column < 0 || column >= clist->columns)
return;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
/* if text is null, then the cell is empty */
GTK_CLIST_GET_CLASS (clist)->set_cell_contents
(clist, clist_row, column, GTK_CELL_TEXT, text, 0, NULL, NULL);
-
+
/* redraw the list if it's not frozen */
if (CLIST_UNFROZEN (clist))
{
@@ -2216,23 +2215,23 @@ gtk_clist_get_text (GtkCList *clist,
gchar **text)
{
GtkCListRow *clist_row;
-
+
g_return_val_if_fail (clist != NULL, 0);
g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
-
+
if (row < 0 || row >= clist->rows)
return 0;
if (column < 0 || column >= clist->columns)
return 0;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
if (clist_row->cell[column].type != GTK_CELL_TEXT)
return 0;
-
+
if (text)
*text = GTK_CELL_TEXT (clist_row->cell[column])->text;
-
+
return 1;
}
@@ -2244,15 +2243,15 @@ gtk_clist_set_pixmap (GtkCList *clist,
GdkBitmap *mask)
{
GtkCListRow *clist_row;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (row < 0 || row >= clist->rows)
return;
if (column < 0 || column >= clist->columns)
return;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
gdk_pixmap_ref (pixmap);
@@ -2261,7 +2260,7 @@ gtk_clist_set_pixmap (GtkCList *clist,
GTK_CLIST_GET_CLASS (clist)->set_cell_contents
(clist, clist_row, column, GTK_CELL_PIXMAP, NULL, 0, pixmap, mask);
-
+
/* redraw the list if it's not frozen */
if (CLIST_UNFROZEN (clist))
{
@@ -2278,27 +2277,27 @@ gtk_clist_get_pixmap (GtkCList *clist,
GdkBitmap **mask)
{
GtkCListRow *clist_row;
-
+
g_return_val_if_fail (clist != NULL, 0);
g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
-
+
if (row < 0 || row >= clist->rows)
return 0;
if (column < 0 || column >= clist->columns)
return 0;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
if (clist_row->cell[column].type != GTK_CELL_PIXMAP)
return 0;
-
+
if (pixmap)
- {
- *pixmap = GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap;
- /* mask can be NULL */
- *mask = GTK_CELL_PIXMAP (clist_row->cell[column])->mask;
- }
-
+ {
+ *pixmap = GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap;
+ /* mask can be NULL */
+ *mask = GTK_CELL_PIXMAP (clist_row->cell[column])->mask;
+ }
+
return 1;
}
@@ -2312,22 +2311,22 @@ gtk_clist_set_pixtext (GtkCList *clist,
GdkBitmap *mask)
{
GtkCListRow *clist_row;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (row < 0 || row >= clist->rows)
return;
if (column < 0 || column >= clist->columns)
return;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
gdk_pixmap_ref (pixmap);
if (mask) gdk_pixmap_ref (mask);
GTK_CLIST_GET_CLASS (clist)->set_cell_contents
(clist, clist_row, column, GTK_CELL_PIXTEXT, text, spacing, pixmap, mask);
-
+
/* redraw the list if it's not frozen */
if (CLIST_UNFROZEN (clist))
{
@@ -2346,30 +2345,30 @@ gtk_clist_get_pixtext (GtkCList *clist,
GdkBitmap **mask)
{
GtkCListRow *clist_row;
-
+
g_return_val_if_fail (clist != NULL, 0);
g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
-
+
if (row < 0 || row >= clist->rows)
return 0;
if (column < 0 || column >= clist->columns)
return 0;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
if (clist_row->cell[column].type != GTK_CELL_PIXTEXT)
return 0;
-
+
if (text)
*text = GTK_CELL_PIXTEXT (clist_row->cell[column])->text;
if (spacing)
*spacing = GTK_CELL_PIXTEXT (clist_row->cell[column])->spacing;
if (pixmap)
*pixmap = GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap;
-
+
/* mask can be NULL */
*mask = GTK_CELL_PIXTEXT (clist_row->cell[column])->mask;
-
+
return 1;
}
@@ -2382,27 +2381,27 @@ gtk_clist_set_shift (GtkCList *clist,
{
GtkRequisition requisition = { 0 };
GtkCListRow *clist_row;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (row < 0 || row >= clist->rows)
return;
if (column < 0 || column >= clist->columns)
return;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
if (clist->column[column].auto_resize &&
!GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
GTK_CLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
- column, &requisition);
-
+ column, &requisition);
+
clist_row->cell[column].vertical = vertical;
clist_row->cell[column].horizontal = horizontal;
-
+
column_auto_resize (clist, clist_row, column, requisition.width);
-
+
if (CLIST_UNFROZEN (clist) && gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
}
@@ -2422,16 +2421,16 @@ set_cell_contents (GtkCList *clist,
GdkBitmap *mask)
{
GtkRequisition requisition;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
g_return_if_fail (clist_row != NULL);
-
+
if (clist->column[column].auto_resize &&
!GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
GTK_CLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
- column, &requisition);
-
+ column, &requisition);
+
switch (clist_row->cell[column].type)
{
case GTK_CELL_EMPTY:
@@ -2456,9 +2455,9 @@ set_cell_contents (GtkCList *clist,
default:
break;
}
-
+
clist_row->cell[column].type = GTK_CELL_EMPTY;
-
+
switch (type)
{
case GTK_CELL_TEXT:
@@ -2490,60 +2489,104 @@ set_cell_contents (GtkCList *clist,
default:
break;
}
-
+
if (clist->column[column].auto_resize &&
!GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
column_auto_resize (clist, clist_row, column, requisition.width);
}
+PangoLayout *
+_gtk_clist_create_cell_layout (GtkCList *clist,
+ GtkCListRow *clist_row,
+ gint column)
+{
+ PangoContext *context;
+ PangoLayout *layout;
+ GtkStyle *style;
+ GtkCell *cell;
+ gchar *text;
+
+ get_cell_style (clist, clist_row, GTK_STATE_NORMAL, column, &style,
+ NULL, NULL);
+
+
+ cell = &clist_row->cell[column];
+ switch (cell->type)
+ {
+ case GTK_CELL_TEXT:
+ case GTK_CELL_PIXTEXT:
+ text = ((cell->type == GTK_CELL_PIXTEXT) ?
+ GTK_CELL_PIXTEXT (*cell)->text :
+ GTK_CELL_TEXT (*cell)->text);
+
+ if (!text)
+ return NULL;
+
+ context = gtk_widget_create_pango_context (GTK_WIDGET (clist));
+ pango_context_set_font_description (context, style->font_desc);
+
+ layout = pango_layout_new (context);
+ pango_layout_set_text (layout, ((cell->type == GTK_CELL_PIXTEXT) ?
+ GTK_CELL_PIXTEXT (*cell)->text :
+ GTK_CELL_TEXT (*cell)->text), -1);
+
+ pango_context_unref (context);
+
+ return layout;
+
+ default:
+ return NULL;
+ }
+}
+
static void
cell_size_request (GtkCList *clist,
GtkCListRow *clist_row,
gint column,
GtkRequisition *requisition)
{
- GtkStyle *style;
gint width;
gint height;
-
+ PangoLayout *layout;
+ PangoRectangle logical_rect;
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
g_return_if_fail (requisition != NULL);
-
- get_cell_style (clist, clist_row, GTK_STATE_NORMAL, column, &style,
- NULL, NULL);
-
+
+ layout = _gtk_clist_create_cell_layout (clist, clist_row, column);
+ if (layout)
+ {
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+
+ requisition->width = logical_rect.width / PANGO_SCALE;
+ requisition->height = logical_rect.height / PANGO_SCALE;
+
+ pango_layout_unref (layout);
+ }
+ else
+ {
+ requisition->width = 0;
+ requisition->height = 0;
+ }
+
+ if (layout && clist_row->cell[column].type == GTK_CELL_PIXTEXT)
+ requisition->width += GTK_CELL_PIXTEXT (clist_row->cell[column])->spacing;
+
switch (clist_row->cell[column].type)
{
- case GTK_CELL_TEXT:
- requisition->width =
- gdk_string_width (style->font,
- GTK_CELL_TEXT (clist_row->cell[column])->text);
- requisition->height = style->font->ascent + style->font->descent;
- break;
case GTK_CELL_PIXTEXT:
- gdk_window_get_size (GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap,
- &width, &height);
- requisition->width = width +
- GTK_CELL_PIXTEXT (clist_row->cell[column])->spacing +
- gdk_string_width (style->font,
- GTK_CELL_TEXT (clist_row->cell[column])->text);
-
- requisition->height = MAX (style->font->ascent + style->font->descent,
- height);
- break;
case GTK_CELL_PIXMAP:
gdk_window_get_size (GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap,
&width, &height);
- requisition->width = width;
- requisition->height = height;
+ requisition->width += width;
+ requisition->height = MAX (requisition->height, height);
break;
+
default:
- requisition->width = 0;
- requisition->height = 0;
break;
}
-
+
requisition->width += clist_row->cell[column].horizontal;
requisition->height += clist_row->cell[column].vertical;
}
@@ -2562,7 +2605,7 @@ gtk_clist_prepend (GtkCList *clist,
g_return_val_if_fail (clist != NULL, -1);
g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
g_return_val_if_fail (text != NULL, -1);
-
+
return GTK_CLIST_GET_CLASS (clist)->insert_row (clist, 0, text);
}
@@ -2573,7 +2616,7 @@ gtk_clist_append (GtkCList *clist,
g_return_val_if_fail (clist != NULL, -1);
g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
g_return_val_if_fail (text != NULL, -1);
-
+
return GTK_CLIST_GET_CLASS (clist)->insert_row (clist, clist->rows, text);
}
@@ -2585,10 +2628,10 @@ gtk_clist_insert (GtkCList *clist,
g_return_val_if_fail (clist != NULL, -1);
g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
g_return_val_if_fail (text != NULL, -1);
-
+
if (row < 0 || row > clist->rows)
row = clist->rows;
-
+
return GTK_CLIST_GET_CLASS (clist)->insert_row (clist, row, text);
}
@@ -2621,24 +2664,24 @@ real_insert_row (GtkCList *clist,
{
gint i;
GtkCListRow *clist_row;
-
+
g_return_val_if_fail (clist != NULL, -1);
g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
g_return_val_if_fail (text != NULL, -1);
-
+
/* return if out of bounds */
if (row < 0 || row > clist->rows)
return -1;
-
+
/* create the row */
clist_row = row_new (clist);
-
+
/* set the text in the row's columns */
for (i = 0; i < clist->columns; i++)
if (text[i])
GTK_CLIST_GET_CLASS (clist)->set_cell_contents
(clist, clist_row, i, GTK_CELL_TEXT, text[i], 0, NULL ,NULL);
-
+
if (!clist->rows)
{
clist->row_list = g_list_append (clist->row_list, clist_row);
@@ -2681,32 +2724,32 @@ real_insert_row (GtkCList *clist,
clist_row))->next;
else
clist->row_list = g_list_insert (clist->row_list, clist_row, row);
-
+
}
clist->rows++;
-
+
if (row < ROW_FROM_YPIXEL (clist, 0))
clist->voffset -= (clist->row_height + CELL_SPACING);
-
+
/* syncronize the selection list */
sync_selection (clist, row, SYNC_INSERT);
-
+
if (clist->rows == 1)
{
clist->focus_row = 0;
if (clist->selection_mode == GTK_SELECTION_BROWSE)
gtk_clist_select_row (clist, 0, -1);
}
-
+
/* redraw the list if it isn't frozen */
if (CLIST_UNFROZEN (clist))
{
adjust_adjustments (clist, FALSE);
-
+
if (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
draw_rows (clist, NULL);
}
-
+
return row;
}
@@ -2717,22 +2760,22 @@ real_remove_row (GtkCList *clist,
gint was_visible, was_selected;
GList *list;
GtkCListRow *clist_row;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
/* return if out of bounds */
if (row < 0 || row > (clist->rows - 1))
return;
-
+
was_visible = (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE);
was_selected = 0;
-
+
/* get the row we're going to delete */
list = ROW_ELEMENT (clist, row);
g_assert (list != NULL);
clist_row = list->data;
-
+
/* if we're removing a selected row, we have to make sure
* it's properly unselected, and then sync up the clist->selected
* list to reflect the deincrimented indexies of rows after the
@@ -2740,7 +2783,7 @@ real_remove_row (GtkCList *clist,
if (clist_row->state == GTK_STATE_SELECTED)
gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
row, -1, NULL);
-
+
/* reset the row end pointer if we're removing at the end of the list */
clist->rows--;
if (clist->row_list == list)
@@ -2748,29 +2791,29 @@ real_remove_row (GtkCList *clist,
if (clist->row_list_end == list)
clist->row_list_end = g_list_previous (list);
g_list_remove (list, clist_row);
-
+
/*if (clist->focus_row >=0 &&
- (row <= clist->focus_row || clist->focus_row >= clist->rows))
- clist->focus_row--;*/
-
+ (row <= clist->focus_row || clist->focus_row >= clist->rows))
+ clist->focus_row--;*/
+
if (row < ROW_FROM_YPIXEL (clist, 0))
clist->voffset += clist->row_height + CELL_SPACING;
-
+
sync_selection (clist, row, SYNC_REMOVE);
-
+
if (clist->selection_mode == GTK_SELECTION_BROWSE && !clist->selection &&
clist->focus_row >= 0)
gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
clist->focus_row, -1, NULL);
-
+
/* toast the row */
row_delete (clist, clist_row);
-
+
/* redraw the row if it isn't frozen */
if (CLIST_UNFROZEN (clist))
{
adjust_adjustments (clist, FALSE);
-
+
if (was_visible)
draw_rows (clist, NULL);
}
@@ -2782,15 +2825,15 @@ real_clear (GtkCList *clist)
GList *list;
GList *free_list;
gint i;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
/* free up the selection list */
g_list_free (clist->selection);
g_list_free (clist->undo_selection);
g_list_free (clist->undo_unselection);
-
+
clist->selection = NULL;
clist->selection_end = NULL;
clist->undo_selection = NULL;
@@ -2801,7 +2844,7 @@ real_clear (GtkCList *clist)
clist->undo_anchor = -1;
clist->anchor_state = GTK_STATE_SELECTED;
clist->drag_pos = -1;
-
+
/* remove all the rows */
GTK_CLIST_SET_FLAG (clist, CLIST_AUTO_RESIZE_BLOCKED);
free_list = clist->row_list;
@@ -2841,33 +2884,33 @@ real_row_move (GtkCList *clist,
GList *list;
gint first, last;
gint d;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (GTK_CLIST_AUTO_SORT(clist))
return;
-
+
if (source_row < 0 || source_row >= clist->rows ||
dest_row < 0 || dest_row >= clist->rows ||
source_row == dest_row)
return;
-
+
gtk_clist_freeze (clist);
-
+
/* unlink source row */
clist_row = ROW_ELEMENT (clist, source_row)->data;
if (source_row == clist->rows - 1)
clist->row_list_end = clist->row_list_end->prev;
clist->row_list = g_list_remove (clist->row_list, clist_row);
clist->rows--;
-
+
/* relink source row */
clist->row_list = g_list_insert (clist->row_list, clist_row, dest_row);
if (dest_row == clist->rows)
clist->row_list_end = clist->row_list_end->next;
clist->rows++;
-
+
/* sync selection */
if (source_row > dest_row)
{
@@ -2881,7 +2924,7 @@ real_row_move (GtkCList *clist,
last = dest_row;
d = -1;
}
-
+
for (list = clist->selection; list; list = list->next)
{
if (list->data == GINT_TO_POINTER (source_row))
@@ -2895,7 +2938,7 @@ real_row_move (GtkCList *clist,
clist->focus_row = dest_row;
else if (clist->focus_row > first)
clist->focus_row += d;
-
+
gtk_clist_thaw (clist);
}
@@ -2921,20 +2964,20 @@ gtk_clist_moveto (GtkCList *clist,
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (row < -1 || row >= clist->rows)
return;
if (column < -1 || column >= clist->columns)
return;
-
+
row_align = CLAMP (row_align, 0, 1);
col_align = CLAMP (col_align, 0, 1);
-
+
/* adjust horizontal scrollbar */
if (clist->hadjustment && column >= 0)
{
gint x;
-
+
x = (COLUMN_LEFT (clist, column) - CELL_SPACING - COLUMN_INSET -
(col_align * (clist->clist_window_width - 2 * COLUMN_INSET -
CELL_SPACING - clist->column[column].area.width)));
@@ -2946,7 +2989,7 @@ gtk_clist_moveto (GtkCList *clist,
else
gtk_adjustment_set_value (clist->hadjustment, x);
}
-
+
/* adjust vertical scrollbar */
if (clist->vadjustment && row >= 0)
move_vertical (clist, row, row_align);
@@ -2957,12 +3000,12 @@ gtk_clist_set_row_height (GtkCList *clist,
guint height)
{
GtkWidget *widget;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
widget = GTK_WIDGET (clist);
-
+
if (height > 0)
{
clist->row_height = height;
@@ -2973,21 +3016,24 @@ gtk_clist_set_row_height (GtkCList *clist,
GTK_CLIST_UNSET_FLAG (clist, CLIST_ROW_HEIGHT_SET);
clist->row_height = 0;
}
-
- if (GTK_WIDGET_REALIZED (clist))
+
+ if (widget->style->font_desc)
{
+ PangoContext *context = gtk_widget_create_pango_context (widget);
+ PangoFontMetrics metrics;
+ PangoFont *font = pango_context_load_font (context, widget->style->font_desc);
+ gchar *lang = pango_context_get_lang (context);
+
+ pango_font_get_metrics (font, lang, &metrics);
+
+ g_free (lang);
+ g_object_unref (G_OBJECT (font));
+ pango_context_unref (context);
+
if (!GTK_CLIST_ROW_HEIGHT_SET(clist))
- {
- clist->row_height = (widget->style->font->ascent +
- widget->style->font->descent + 1);
- clist->row_center_offset = widget->style->font->ascent + 1.5;
- }
- else
- clist->row_center_offset = 1.5 + (clist->row_height +
- widget->style->font->ascent -
- widget->style->font->descent - 1) / 2;
+ clist->row_height = (metrics.ascent + metrics.descent) / PANGO_SCALE;
}
-
+
CLIST_REFRESH (clist);
}
@@ -3006,15 +3052,15 @@ gtk_clist_set_row_data_full (GtkCList *clist,
GtkDestroyNotify destroy)
{
GtkCListRow *clist_row;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (row < 0 || row > (clist->rows - 1))
return;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
if (clist_row->destroy)
clist_row->destroy (clist_row->data);
@@ -3027,13 +3073,13 @@ gtk_clist_get_row_data (GtkCList *clist,
gint row)
{
GtkCListRow *clist_row;
-
+
g_return_val_if_fail (clist != NULL, NULL);
g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
-
+
if (row < 0 || row > (clist->rows - 1))
return NULL;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
return clist_row->data;
}
@@ -3044,14 +3090,14 @@ gtk_clist_find_row_from_data (GtkCList *clist,
{
GList *list;
gint n;
-
+
g_return_val_if_fail (clist != NULL, -1);
g_return_val_if_fail (GTK_IS_CLIST (clist), -1);
-
+
for (n = 0, list = clist->row_list; list; n++, list = list->next)
if (GTK_CLIST_ROW (list)->data == data)
return n;
-
+
return -1;
}
@@ -3061,19 +3107,19 @@ gtk_clist_swap_rows (GtkCList *clist,
gint row2)
{
gint first, last;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
g_return_if_fail (row1 != row2);
-
+
if (GTK_CLIST_AUTO_SORT(clist))
return;
-
+
gtk_clist_freeze (clist);
-
+
first = MIN (row1, row2);
last = MAX (row1, row2);
-
+
gtk_clist_row_move (clist, last, first);
gtk_clist_row_move (clist, first + 1, last);
@@ -3087,15 +3133,15 @@ gtk_clist_row_move (GtkCList *clist,
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (GTK_CLIST_AUTO_SORT(clist))
return;
-
+
if (source_row < 0 || source_row >= clist->rows ||
dest_row < 0 || dest_row >= clist->rows ||
source_row == dest_row)
return;
-
+
gtk_signal_emit (GTK_OBJECT (clist), clist_signals[ROW_MOVE],
source_row, dest_row);
}
@@ -3105,28 +3151,28 @@ gtk_clist_row_is_visible (GtkCList *clist,
gint row)
{
gint top;
-
+
g_return_val_if_fail (clist != NULL, 0);
g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
-
+
if (row < 0 || row >= clist->rows)
return GTK_VISIBILITY_NONE;
-
+
if (clist->row_height == 0)
return GTK_VISIBILITY_NONE;
-
+
if (row < ROW_FROM_YPIXEL (clist, 0))
return GTK_VISIBILITY_NONE;
-
+
if (row > ROW_FROM_YPIXEL (clist, clist->clist_window_height))
return GTK_VISIBILITY_NONE;
-
+
top = ROW_TOP_YPIXEL (clist, row);
-
+
if ((top < 0)
|| ((top + clist->row_height) >= clist->clist_window_height))
return GTK_VISIBILITY_PARTIAL;
-
+
return GTK_VISIBILITY_FULL;
}
@@ -3136,15 +3182,15 @@ gtk_clist_set_foreground (GtkCList *clist,
GdkColor *color)
{
GtkCListRow *clist_row;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (row < 0 || row >= clist->rows)
return;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
if (color)
{
clist_row->foreground = *color;
@@ -3155,7 +3201,7 @@ gtk_clist_set_foreground (GtkCList *clist,
}
else
clist_row->fg_set = FALSE;
-
+
if (CLIST_UNFROZEN (clist) && gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
}
@@ -3166,15 +3212,15 @@ gtk_clist_set_background (GtkCList *clist,
GdkColor *color)
{
GtkCListRow *clist_row;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (row < 0 || row >= clist->rows)
return;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
if (color)
{
clist_row->background = *color;
@@ -3185,7 +3231,7 @@ gtk_clist_set_background (GtkCList *clist,
}
else
clist_row->bg_set = FALSE;
-
+
if (CLIST_UNFROZEN (clist)
&& (gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE))
GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row, clist_row);
@@ -3205,34 +3251,34 @@ gtk_clist_set_cell_style (GtkCList *clist,
{
GtkRequisition requisition = { 0 };
GtkCListRow *clist_row;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (row < 0 || row >= clist->rows)
return;
if (column < 0 || column >= clist->columns)
return;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
if (clist_row->cell[column].style == style)
return;
-
+
if (clist->column[column].auto_resize &&
!GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
GTK_CLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
- column, &requisition);
-
+ column, &requisition);
+
if (clist_row->cell[column].style)
{
if (GTK_WIDGET_REALIZED (clist))
gtk_style_detach (clist_row->cell[column].style);
gtk_style_unref (clist_row->cell[column].style);
}
-
+
clist_row->cell[column].style = style;
-
+
if (clist_row->cell[column].style)
{
gtk_style_ref (clist_row->cell[column].style);
@@ -3242,9 +3288,9 @@ gtk_clist_set_cell_style (GtkCList *clist,
gtk_style_attach (clist_row->cell[column].style,
clist->clist_window);
}
-
+
column_auto_resize (clist, clist_row, column, requisition.width);
-
+
/* redraw the list if it's not frozen */
if (CLIST_UNFROZEN (clist))
{
@@ -3259,15 +3305,15 @@ gtk_clist_get_cell_style (GtkCList *clist,
gint column)
{
GtkCListRow *clist_row;
-
+
g_return_val_if_fail (clist != NULL, NULL);
g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
-
+
if (row < 0 || row >= clist->rows || column < 0 || column >= clist->columns)
return NULL;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
return clist_row->cell[column].style;
}
@@ -3280,40 +3326,40 @@ gtk_clist_set_row_style (GtkCList *clist,
GtkCListRow *clist_row;
gint *old_width;
gint i;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (row < 0 || row >= clist->rows)
return;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
if (clist_row->style == style)
return;
-
+
old_width = g_new (gint, clist->columns);
-
+
if (!GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
{
for (i = 0; i < clist->columns; i++)
if (clist->column[i].auto_resize)
{
GTK_CLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
- i, &requisition);
+ i, &requisition);
old_width[i] = requisition.width;
}
}
-
+
if (clist_row->style)
{
if (GTK_WIDGET_REALIZED (clist))
gtk_style_detach (clist_row->style);
gtk_style_unref (clist_row->style);
}
-
+
clist_row->style = style;
-
+
if (clist_row->style)
{
gtk_style_ref (clist_row->style);
@@ -3322,13 +3368,13 @@ gtk_clist_set_row_style (GtkCList *clist,
clist_row->style = gtk_style_attach (clist_row->style,
clist->clist_window);
}
-
+
if (GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
for (i = 0; i < clist->columns; i++)
column_auto_resize (clist, clist_row, i, old_width[i]);
-
+
g_free (old_width);
-
+
/* redraw the list if it's not frozen */
if (CLIST_UNFROZEN (clist))
{
@@ -3342,15 +3388,15 @@ gtk_clist_get_row_style (GtkCList *clist,
gint row)
{
GtkCListRow *clist_row;
-
+
g_return_val_if_fail (clist != NULL, NULL);
g_return_val_if_fail (GTK_IS_CLIST (clist), NULL);
-
+
if (row < 0 || row >= clist->rows)
return NULL;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
return clist_row->style;
}
@@ -3369,20 +3415,20 @@ gtk_clist_set_selectable (GtkCList *clist,
gboolean selectable)
{
GtkCListRow *clist_row;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (row < 0 || row >= clist->rows)
return;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
if (selectable == clist_row->selectable)
return;
-
+
clist_row->selectable = selectable;
-
+
if (!selectable && clist_row->state == GTK_STATE_SELECTED)
{
if (clist->anchor >= 0 &&
@@ -3403,10 +3449,10 @@ gtk_clist_get_selectable (GtkCList *clist,
{
g_return_val_if_fail (clist != NULL, FALSE);
g_return_val_if_fail (GTK_IS_CLIST (clist), FALSE);
-
+
if (row < 0 || row >= clist->rows)
return FALSE;
-
+
return GTK_CLIST_ROW (ROW_ELEMENT (clist, row))->selectable;
}
@@ -3417,12 +3463,12 @@ gtk_clist_select_row (GtkCList *clist,
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (row < 0 || row >= clist->rows)
return;
if (column < -1 || column >= clist->columns)
return;
-
+
gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
row, column, NULL);
}
@@ -3434,12 +3480,12 @@ gtk_clist_unselect_row (GtkCList *clist,
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (row < 0 || row >= clist->rows)
return;
if (column < -1 || column >= clist->columns)
return;
-
+
gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
row, column, NULL);
}
@@ -3449,7 +3495,7 @@ gtk_clist_select_all (GtkCList *clist)
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
GTK_CLIST_GET_CLASS (clist)->select_all (clist);
}
@@ -3458,7 +3504,7 @@ gtk_clist_unselect_all (GtkCList *clist)
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
GTK_CLIST_GET_CLASS (clist)->unselect_all (clist);
}
@@ -3467,7 +3513,7 @@ gtk_clist_undo_selection (GtkCList *clist)
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (clist->selection_mode == GTK_SELECTION_EXTENDED &&
(clist->undo_selection || clist->undo_unselection))
gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNDO_SELECTION]);
@@ -3508,17 +3554,17 @@ toggle_row (GtkCList *clist,
GdkEvent *event)
{
GtkCListRow *clist_row;
-
+
switch (clist->selection_mode)
{
case GTK_SELECTION_EXTENDED:
case GTK_SELECTION_MULTIPLE:
case GTK_SELECTION_SINGLE:
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
if (!clist_row)
return;
-
+
if (clist_row->state == GTK_STATE_SELECTED)
{
gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
@@ -3537,9 +3583,9 @@ fake_toggle_row (GtkCList *clist,
gint row)
{
GList *work;
-
+
work = ROW_ELEMENT (clist, row);
-
+
if (!work || !GTK_CLIST_ROW (work)->selectable)
return;
@@ -3551,7 +3597,7 @@ fake_toggle_row (GtkCList *clist,
if (CLIST_UNFROZEN (clist) &&
gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row,
- GTK_CLIST_ROW (work));
+ GTK_CLIST_ROW (work));
}
static void
@@ -3559,11 +3605,11 @@ toggle_focus_row (GtkCList *clist)
{
g_return_if_fail (clist != 0);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)) ||
clist->focus_row < 0 || clist->focus_row >= clist->rows)
return;
-
+
switch (clist->selection_mode)
{
case GTK_SELECTION_SINGLE:
@@ -3575,7 +3621,7 @@ toggle_focus_row (GtkCList *clist)
g_list_free (clist->undo_unselection);
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
-
+
clist->anchor = clist->focus_row;
clist->drag_pos = clist->focus_row;
clist->undo_anchor = clist->focus_row;
@@ -3584,7 +3630,7 @@ toggle_focus_row (GtkCList *clist)
fake_toggle_row (clist, clist->focus_row);
else
GTK_CLIST_GET_CLASS (clist)->fake_unselect_all (clist,clist->focus_row);
-
+
GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
break;
default:
@@ -3601,7 +3647,7 @@ toggle_add_mode (GtkCList *clist)
if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)) ||
clist->selection_mode != GTK_SELECTION_EXTENDED)
return;
-
+
gtk_clist_draw_focus (GTK_WIDGET (clist));
if (!GTK_CLIST_ADD_MODE(clist))
{
@@ -3629,45 +3675,45 @@ real_select_row (GtkCList *clist,
GList *list;
gint sel_row;
gboolean row_selected;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (row < 0 || row > (clist->rows - 1))
return;
-
+
switch (clist->selection_mode)
{
case GTK_SELECTION_SINGLE:
case GTK_SELECTION_BROWSE:
-
+
row_selected = FALSE;
list = clist->selection;
-
+
while (list)
{
sel_row = GPOINTER_TO_INT (list->data);
list = list->next;
-
+
if (row == sel_row)
row_selected = TRUE;
else
gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
sel_row, column, event);
}
-
+
if (row_selected)
return;
default:
break;
}
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
if (clist_row->state != GTK_STATE_NORMAL || !clist_row->selectable)
return;
-
+
clist_row->state = GTK_STATE_SELECTED;
if (!clist->selection)
{
@@ -3691,23 +3737,23 @@ real_unselect_row (GtkCList *clist,
GdkEvent *event)
{
GtkCListRow *clist_row;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (row < 0 || row > (clist->rows - 1))
return;
-
+
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
if (clist_row->state == GTK_STATE_SELECTED)
{
clist_row->state = GTK_STATE_NORMAL;
-
+
if (clist->selection_end &&
clist->selection_end->data == GINT_TO_POINTER (row))
clist->selection_end = clist->selection_end->prev;
-
+
clist->selection = g_list_remove (clist->selection,
GINT_TO_POINTER (row));
@@ -3722,30 +3768,30 @@ real_select_all (GtkCList *clist)
{
GList *list;
gint i;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
return;
-
+
switch (clist->selection_mode)
{
case GTK_SELECTION_SINGLE:
case GTK_SELECTION_BROWSE:
return;
-
+
case GTK_SELECTION_EXTENDED:
g_list_free (clist->undo_selection);
g_list_free (clist->undo_unselection);
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
-
+
if (clist->rows &&
((GtkCListRow *) (clist->row_list->data))->state !=
GTK_STATE_SELECTED)
fake_toggle_row (clist, 0);
-
+
clist->anchor_state = GTK_STATE_SELECTED;
clist->anchor = 0;
clist->drag_pos = 0;
@@ -3753,7 +3799,7 @@ real_select_all (GtkCList *clist)
update_extended_selection (clist, clist->rows);
GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
return;
-
+
case GTK_SELECTION_MULTIPLE:
for (i = 0, list = clist->row_list; list; i++, list = list->next)
{
@@ -3770,13 +3816,13 @@ real_unselect_all (GtkCList *clist)
{
GList *list;
gint i;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
return;
-
+
switch (clist->selection_mode)
{
case GTK_SELECTION_BROWSE:
@@ -3793,7 +3839,7 @@ real_unselect_all (GtkCList *clist)
g_list_free (clist->undo_unselection);
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
-
+
clist->anchor = -1;
clist->drag_pos = -1;
clist->undo_anchor = clist->focus_row;
@@ -3801,7 +3847,7 @@ real_unselect_all (GtkCList *clist)
default:
break;
}
-
+
list = clist->selection;
while (list)
{
@@ -3819,7 +3865,7 @@ fake_unselect_all (GtkCList *clist,
GList *list;
GList *work;
gint i;
-
+
if (row >= 0 && (work = ROW_ELEMENT (clist, row)))
{
if (GTK_CLIST_ROW (work)->state == GTK_STATE_NORMAL &&
@@ -3830,25 +3876,25 @@ fake_unselect_all (GtkCList *clist,
if (CLIST_UNFROZEN (clist) &&
gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row,
- GTK_CLIST_ROW (work));
+ GTK_CLIST_ROW (work));
}
}
-
+
clist->undo_selection = clist->selection;
clist->selection = NULL;
clist->selection_end = NULL;
-
+
for (list = clist->undo_selection; list; list = list->next)
{
if ((i = GPOINTER_TO_INT (list->data)) == row ||
!(work = g_list_nth (clist->row_list, i)))
continue;
-
+
GTK_CLIST_ROW (work)->state = GTK_STATE_NORMAL;
if (CLIST_UNFROZEN (clist) &&
gtk_clist_row_is_visible (clist, i) != GTK_VISIBILITY_NONE)
GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, i,
- GTK_CLIST_ROW (work));
+ GTK_CLIST_ROW (work));
}
}
@@ -3856,33 +3902,33 @@ static void
real_undo_selection (GtkCList *clist)
{
GList *work;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)) ||
clist->selection_mode != GTK_SELECTION_EXTENDED)
return;
-
+
GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
-
+
if (!(clist->undo_selection || clist->undo_unselection))
{
gtk_clist_unselect_all (clist);
return;
}
-
+
for (work = clist->undo_selection; work; work = work->next)
gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
GPOINTER_TO_INT (work->data), -1, NULL);
-
+
for (work = clist->undo_unselection; work; work = work->next)
{
/* g_print ("unselect %d\n",GPOINTER_TO_INT (work->data)); */
gtk_signal_emit (GTK_OBJECT (clist), clist_signals[UNSELECT_ROW],
GPOINTER_TO_INT (work->data), -1, NULL);
}
-
+
if (GTK_WIDGET_HAS_FOCUS(clist) && clist->focus_row != clist->undo_anchor)
{
gtk_clist_draw_focus (GTK_WIDGET (clist));
@@ -3893,12 +3939,12 @@ real_undo_selection (GtkCList *clist)
clist->focus_row = clist->undo_anchor;
clist->undo_anchor = -1;
-
+
g_list_free (clist->undo_selection);
g_list_free (clist->undo_unselection);
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
-
+
if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
clist->clist_window_height)
gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
@@ -3917,12 +3963,12 @@ set_anchor (GtkCList *clist,
if (clist->selection_mode != GTK_SELECTION_EXTENDED || clist->anchor >= 0)
return;
-
+
g_list_free (clist->undo_selection);
g_list_free (clist->undo_unselection);
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
-
+
if (add_mode)
fake_toggle_row (clist, anchor);
else
@@ -3930,7 +3976,7 @@ set_anchor (GtkCList *clist,
GTK_CLIST_GET_CLASS (clist)->fake_unselect_all (clist, anchor);
clist->anchor_state = GTK_STATE_SELECTED;
}
-
+
clist->anchor = anchor;
clist->drag_pos = anchor;
clist->undo_anchor = undo_anchor;
@@ -3945,18 +3991,18 @@ resync_selection (GtkCList *clist,
gint row;
GList *list;
GtkCListRow *clist_row;
-
+
if (clist->selection_mode != GTK_SELECTION_EXTENDED)
return;
-
+
if (clist->anchor < 0 || clist->drag_pos < 0)
return;
-
+
gtk_clist_freeze (clist);
-
+
i = MIN (clist->anchor, clist->drag_pos);
e = MAX (clist->anchor, clist->drag_pos);
-
+
if (clist->undo_selection)
{
list = clist->selection;
@@ -3983,7 +4029,7 @@ resync_selection (GtkCList *clist,
}
}
}
-
+
if (clist->anchor < clist->drag_pos)
{
for (list = g_list_nth (clist->row_list, i); i <= e;
@@ -4045,10 +4091,10 @@ resync_selection (GtkCList *clist,
for (list = clist->undo_unselection; list; list = list->next)
gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
GPOINTER_TO_INT (list->data), -1, event);
-
+
clist->anchor = -1;
clist->drag_pos = -1;
-
+
gtk_clist_thaw (clist);
}
@@ -4068,15 +4114,15 @@ update_extended_selection (GtkCList *clist,
gint h1 = 0;
gint h2 = 0;
gint top;
-
+
if (clist->selection_mode != GTK_SELECTION_EXTENDED || clist->anchor == -1)
return;
-
+
if (row < 0)
row = 0;
if (row >= clist->rows)
row = clist->rows - 1;
-
+
/* extending downwards */
if (row > clist->drag_pos && clist->anchor <= clist->drag_pos)
{
@@ -4121,12 +4167,12 @@ update_extended_selection (GtkCList *clist,
else
e1 = row - 1;
}
-
+
clist->drag_pos = row;
-
+
area.x = 0;
area.width = clist->clist_window_width;
-
+
/* restore the elements between s1 and e1 */
if (s1 >= 0)
{
@@ -4139,9 +4185,9 @@ update_extended_selection (GtkCList *clist,
else
GTK_CLIST_ROW (list)->state = GTK_STATE_NORMAL;
}
-
+
top = ROW_TOP_YPIXEL (clist, clist->focus_row);
-
+
if (top + clist->row_height <= 0)
{
area.y = 0;
@@ -4160,11 +4206,11 @@ update_extended_selection (GtkCList *clist,
gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
else if (top + clist->row_height > clist->clist_window_height)
gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
-
+
y1 = ROW_TOP_YPIXEL (clist, s1) - 1;
h1 = (e1 - s1 + 1) * (clist->row_height + CELL_SPACING);
}
-
+
/* extend the selection between s2 and e2 */
if (s2 >= 0)
{
@@ -4173,9 +4219,9 @@ update_extended_selection (GtkCList *clist,
if (GTK_CLIST_ROW (list)->selectable &&
GTK_CLIST_ROW (list)->state != clist->anchor_state)
GTK_CLIST_ROW (list)->state = clist->anchor_state;
-
+
top = ROW_TOP_YPIXEL (clist, clist->focus_row);
-
+
if (top + clist->row_height <= 0)
{
area.y = 0;
@@ -4194,11 +4240,11 @@ update_extended_selection (GtkCList *clist,
gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
else if (top + clist->row_height > clist->clist_window_height)
gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
-
+
y2 = ROW_TOP_YPIXEL (clist, s2) - 1;
h2 = (e2 - s2 + 1) * (clist->row_height + CELL_SPACING);
}
-
+
area.y = MAX (0, MIN (y1, y2));
if (area.y > clist->clist_window_height)
area.y = 0;
@@ -4213,10 +4259,10 @@ start_selection (GtkCList *clist)
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
return;
-
+
set_anchor (clist, GTK_CLIST_ADD_MODE(clist), clist->focus_row,
clist->focus_row);
}
@@ -4226,10 +4272,10 @@ end_selection (GtkCList *clist)
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_FOCUS(clist))
return;
-
+
GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
}
@@ -4241,25 +4287,25 @@ extend_selection (GtkCList *clist,
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if ((gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)) ||
clist->selection_mode != GTK_SELECTION_EXTENDED)
return;
-
+
if (auto_start_selection)
set_anchor (clist, GTK_CLIST_ADD_MODE(clist), clist->focus_row,
clist->focus_row);
else if (clist->anchor == -1)
return;
-
+
move_focus_row (clist, scroll_type, position);
-
+
if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
clist->clist_window_height)
gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
-
+
update_extended_selection (clist, clist->focus_row);
}
@@ -4270,12 +4316,12 @@ sync_selection (GtkCList *clist,
{
GList *list;
gint d;
-
+
if (mode == SYNC_INSERT)
d = 1;
else
d = -1;
-
+
if (clist->focus_row >= row)
{
if (d > 0 || clist->focus_row > row)
@@ -4285,20 +4331,20 @@ sync_selection (GtkCList *clist,
else if (clist->focus_row >= clist->rows)
clist->focus_row = clist->rows - 1;
}
-
+
GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
-
+
g_list_free (clist->undo_selection);
g_list_free (clist->undo_unselection);
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
-
+
clist->anchor = -1;
clist->drag_pos = -1;
clist->undo_anchor = clist->focus_row;
-
+
list = clist->selection;
-
+
while (list)
{
if (GPOINTER_TO_INT (list->data) >= row)
@@ -4316,25 +4362,25 @@ gtk_clist_destroy (GtkObject *object)
{
gint i;
GtkCList *clist;
-
+
g_return_if_fail (object != NULL);
g_return_if_fail (GTK_IS_CLIST (object));
-
+
clist = GTK_CLIST (object);
-
+
/* freeze the list */
clist->freeze_count++;
-
+
/* get rid of all the rows */
gtk_clist_clear (clist);
-
+
/* Since we don't have a _remove method, unparent the children
* instead of destroying them so the focus will be unset properly.
* (For other containers, the _remove method takes care of the
* unparent) The destroy will happen when the refcount drops
* to zero.
*/
-
+
/* unref adjustments */
if (clist->hadjustment)
{
@@ -4348,9 +4394,9 @@ gtk_clist_destroy (GtkObject *object)
gtk_object_unref (GTK_OBJECT (clist->vadjustment));
clist->vadjustment = NULL;
}
-
+
remove_grab (clist);
-
+
/* destroy the column buttons */
for (i = 0; i < clist->columns; i++)
if (clist->column[i].button)
@@ -4358,7 +4404,7 @@ gtk_clist_destroy (GtkObject *object)
gtk_widget_unparent (clist->column[i].button);
clist->column[i].button = NULL;
}
-
+
if (GTK_OBJECT_CLASS (parent_class)->destroy)
(*GTK_OBJECT_CLASS (parent_class)->destroy) (object);
}
@@ -4367,16 +4413,16 @@ static void
gtk_clist_finalize (GObject *object)
{
GtkCList *clist;
-
+
g_return_if_fail (GTK_IS_CLIST (object));
-
+
clist = GTK_CLIST (object);
-
+
columns_delete (clist);
-
+
g_mem_chunk_destroy (clist->cell_mem_chunk);
g_mem_chunk_destroy (clist->row_mem_chunk);
-
+
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -4407,14 +4453,14 @@ gtk_clist_realize (GtkWidget *widget)
gint border_width;
gint i;
gint j;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CLIST (widget));
-
+
clist = GTK_CLIST (widget);
-
+
GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
-
+
border_width = GTK_CONTAINER (widget)->border_width;
attributes.window_type = GDK_WINDOW_CHILD;
@@ -4432,18 +4478,18 @@ gtk_clist_realize (GtkWidget *widget)
GDK_KEY_PRESS_MASK |
GDK_KEY_RELEASE_MASK);
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
-
+
/* main window */
widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
&attributes, attributes_mask);
gdk_window_set_user_data (widget->window, clist);
-
+
widget->style = gtk_style_attach (widget->style, widget->window);
-
+
gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
-
+
/* column-title window */
-
+
attributes.x = clist->column_title_area.x;
attributes.y = clist->column_title_area.y;
attributes.width = clist->column_title_area.width;
@@ -4452,17 +4498,17 @@ gtk_clist_realize (GtkWidget *widget)
clist->title_window = gdk_window_new (widget->window, &attributes,
attributes_mask);
gdk_window_set_user_data (clist->title_window, clist);
-
+
gtk_style_set_background (widget->style, clist->title_window,
GTK_STATE_NORMAL);
gdk_window_show (clist->title_window);
-
+
/* set things up so column buttons are drawn in title window */
for (i = 0; i < clist->columns; i++)
if (clist->column[i].button)
gtk_widget_set_parent_window (clist->column[i].button,
clist->title_window);
-
+
/* clist-window */
attributes.x = (clist->internal_allocation.x +
widget->style->klass->xthickness);
@@ -4475,13 +4521,13 @@ gtk_clist_realize (GtkWidget *widget)
clist->clist_window = gdk_window_new (widget->window, &attributes,
attributes_mask);
gdk_window_set_user_data (clist->clist_window, clist);
-
+
gdk_window_set_background (clist->clist_window,
&widget->style->base[GTK_STATE_NORMAL]);
gdk_window_show (clist->clist_window);
gdk_window_get_size (clist->clist_window, &clist->clist_window_width,
&clist->clist_window_height);
-
+
/* create resize windows */
attributes.wclass = GDK_INPUT_ONLY;
attributes.event_mask = (GDK_BUTTON_PRESS_MASK |
@@ -4492,31 +4538,31 @@ gtk_clist_realize (GtkWidget *widget)
attributes_mask = GDK_WA_CURSOR;
attributes.cursor = gdk_cursor_new (GDK_SB_H_DOUBLE_ARROW);
clist->cursor_drag = attributes.cursor;
-
+
attributes.x = LIST_WIDTH (clist) + 1;
attributes.y = 0;
attributes.width = 0;
attributes.height = 0;
-
+
for (i = 0; i < clist->columns; i++)
{
clist->column[i].window = gdk_window_new (clist->title_window,
&attributes, attributes_mask);
gdk_window_set_user_data (clist->column[i].window, clist);
}
-
+
/* This is slightly less efficient than creating them with the
* right size to begin with, but easier
*/
size_allocate_title_buttons (clist);
-
+
/* GCs */
clist->fg_gc = gdk_gc_new (widget->window);
clist->bg_gc = gdk_gc_new (widget->window);
/* We'll use this gc to do scrolling as well */
gdk_gc_set_exposures (clist->fg_gc, TRUE);
-
+
values.foreground = (widget->style->white.pixel==0 ?
widget->style->black:widget->style->white);
values.function = GDK_XOR;
@@ -4526,22 +4572,22 @@ gtk_clist_realize (GtkWidget *widget)
GDK_GC_FOREGROUND |
GDK_GC_FUNCTION |
GDK_GC_SUBWINDOW);
-
+
/* attach optional row/cell styles, allocate foreground/background colors */
list = clist->row_list;
for (i = 0; i < clist->rows; i++)
{
clist_row = list->data;
list = list->next;
-
+
if (clist_row->style)
clist_row->style = gtk_style_attach (clist_row->style,
clist->clist_window);
-
+
if (clist_row->fg_set || clist_row->bg_set)
{
GdkColormap *colormap;
-
+
colormap = gtk_widget_get_colormap (widget);
if (clist_row->fg_set)
gdk_color_alloc (colormap, &clist_row->foreground);
@@ -4561,33 +4607,33 @@ gtk_clist_unrealize (GtkWidget *widget)
{
gint i;
GtkCList *clist;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CLIST (widget));
-
+
clist = GTK_CLIST (widget);
-
+
/* freeze the list */
clist->freeze_count++;
-
+
if (GTK_WIDGET_MAPPED (widget))
gtk_clist_unmap (widget);
-
+
GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
-
+
/* detach optional row/cell styles */
if (GTK_WIDGET_REALIZED (widget))
{
GtkCListRow *clist_row;
GList *list;
gint j;
-
+
list = clist->row_list;
for (i = 0; i < clist->rows; i++)
{
clist_row = list->data;
list = list->next;
-
+
if (clist_row->style)
gtk_style_detach (clist_row->style);
for (j = 0; j < clist->columns; j++)
@@ -4595,12 +4641,12 @@ gtk_clist_unrealize (GtkWidget *widget)
gtk_style_detach (clist_row->cell[j].style);
}
}
-
+
gdk_cursor_destroy (clist->cursor_drag);
gdk_gc_destroy (clist->xor_gc);
gdk_gc_destroy (clist->fg_gc);
gdk_gc_destroy (clist->bg_gc);
-
+
for (i = 0; i < clist->columns; i++)
{
if (clist->column[i].button)
@@ -4612,20 +4658,20 @@ gtk_clist_unrealize (GtkWidget *widget)
clist->column[i].window = NULL;
}
}
-
+
gdk_window_set_user_data (clist->clist_window, NULL);
gdk_window_destroy (clist->clist_window);
clist->clist_window = NULL;
-
+
gdk_window_set_user_data (clist->title_window, NULL);
gdk_window_destroy (clist->title_window);
clist->title_window = NULL;
-
+
clist->cursor_drag = NULL;
clist->xor_gc = NULL;
clist->fg_gc = NULL;
clist->bg_gc = NULL;
-
+
if (GTK_WIDGET_CLASS (parent_class)->unrealize)
(* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
}
@@ -4635,16 +4681,16 @@ gtk_clist_map (GtkWidget *widget)
{
gint i;
GtkCList *clist;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CLIST (widget));
-
+
clist = GTK_CLIST (widget);
-
+
if (!GTK_WIDGET_MAPPED (widget))
{
GTK_WIDGET_SET_FLAGS (widget, GTK_MAPPED);
-
+
/* map column buttons */
for (i = 0; i < clist->columns; i++)
{
@@ -4660,11 +4706,11 @@ gtk_clist_map (GtkWidget *widget)
gdk_window_raise (clist->column[i].window);
gdk_window_show (clist->column[i].window);
}
-
+
gdk_window_show (clist->title_window);
gdk_window_show (clist->clist_window);
gdk_window_show (widget->window);
-
+
/* unfreeze the list */
clist->freeze_count = 0;
}
@@ -4675,30 +4721,30 @@ gtk_clist_unmap (GtkWidget *widget)
{
gint i;
GtkCList *clist;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CLIST (widget));
-
+
clist = GTK_CLIST (widget);
-
+
if (GTK_WIDGET_MAPPED (widget))
{
GTK_WIDGET_UNSET_FLAGS (widget, GTK_MAPPED);
-
+
if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
{
remove_grab (clist);
-
+
GTK_CLIST_GET_CLASS (widget)->resync_selection (clist, NULL);
-
+
clist->click_cell.row = -1;
clist->click_cell.column = -1;
clist->drag_button = 0;
-
+
if (GTK_CLIST_IN_DRAG(clist))
{
gpointer drag_data;
-
+
GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
drag_data = gtk_object_get_data (GTK_OBJECT (clist),
"gtk-site-data");
@@ -4707,21 +4753,21 @@ gtk_clist_unmap (GtkWidget *widget)
drag_data);
}
}
-
+
for (i = 0; i < clist->columns; i++)
if (clist->column[i].window)
gdk_window_hide (clist->column[i].window);
-
+
gdk_window_hide (clist->clist_window);
gdk_window_hide (clist->title_window);
gdk_window_hide (widget->window);
-
+
/* unmap column buttons */
for (i = 0; i < clist->columns; i++)
if (clist->column[i].button &&
GTK_WIDGET_MAPPED (clist->column[i].button))
gtk_widget_unmap (clist->column[i].button);
-
+
/* freeze the list */
clist->freeze_count++;
}
@@ -4735,21 +4781,21 @@ gtk_clist_draw (GtkWidget *widget,
gint border_width;
GdkRectangle child_area;
int i;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CLIST (widget));
g_return_if_fail (area != NULL);
-
+
if (GTK_WIDGET_DRAWABLE (widget))
{
clist = GTK_CLIST (widget);
border_width = GTK_CONTAINER (widget)->border_width;
-
+
gdk_window_clear_area (widget->window,
area->x - border_width,
area->y - border_width,
area->width, area->height);
-
+
/* draw list shadow/border */
gtk_draw_shadow (widget->style, widget->window,
GTK_STATE_NORMAL, clist->shadow_type,
@@ -4759,10 +4805,10 @@ gtk_clist_draw (GtkWidget *widget,
clist->clist_window_height +
(2 * widget->style->klass->ythickness) +
clist->column_title_area.height);
-
+
gdk_window_clear_area (clist->clist_window, 0, 0, 0, 0);
draw_rows (clist, NULL);
-
+
for (i = 0; i < clist->columns; i++)
{
if (!clist->column[i].visible)
@@ -4779,15 +4825,15 @@ gtk_clist_expose (GtkWidget *widget,
GdkEventExpose *event)
{
GtkCList *clist;
-
+
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
-
+
if (GTK_WIDGET_DRAWABLE (widget))
{
clist = GTK_CLIST (widget);
-
+
/* draw border */
if (event->window == widget->window)
gtk_draw_shadow (widget->style, widget->window,
@@ -4798,12 +4844,12 @@ gtk_clist_expose (GtkWidget *widget,
clist->clist_window_height +
(2 * widget->style->klass->ythickness) +
clist->column_title_area.height);
-
+
/* exposure events on the list */
if (event->window == clist->clist_window)
draw_rows (clist, &event->area);
}
-
+
return FALSE;
}
@@ -4812,42 +4858,35 @@ gtk_clist_style_set (GtkWidget *widget,
GtkStyle *previous_style)
{
GtkCList *clist;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CLIST (widget));
-
+
if (GTK_WIDGET_CLASS (parent_class)->style_set)
(*GTK_WIDGET_CLASS (parent_class)->style_set) (widget, previous_style);
-
+
clist = GTK_CLIST (widget);
-
+
if (GTK_WIDGET_REALIZED (widget))
{
gtk_style_set_background (widget->style, widget->window, widget->state);
gtk_style_set_background (widget->style, clist->title_window, GTK_STATE_SELECTED);
gdk_window_set_background (clist->clist_window, &widget->style->base[GTK_STATE_NORMAL]);
}
-
+
/* Fill in data after widget has correct style */
-
+
/* text properties */
if (!GTK_CLIST_ROW_HEIGHT_SET(clist))
- {
- clist->row_height = (widget->style->font->ascent +
- widget->style->font->descent + 1);
- clist->row_center_offset = widget->style->font->ascent + 1.5;
- }
- else
- clist->row_center_offset = 1.5 + (clist->row_height +
- widget->style->font->ascent -
- widget->style->font->descent - 1) / 2;
-
+ /* Reset clist->row_height */
+ gtk_clist_set_row_height (clist, 0);
+
/* Column widths */
if (!GTK_CLIST_AUTO_RESIZE_BLOCKED(clist))
{
gint width;
gint i;
-
+
for (i = 0; i < clist->columns; i++)
if (clist->column[i].auto_resize)
{
@@ -4865,11 +4904,11 @@ gtk_clist_key_press (GtkWidget *widget,
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
-
+
if (GTK_WIDGET_CLASS (parent_class)->key_press_event &&
GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
return TRUE;
-
+
switch (event->keyval)
{
case GDK_Tab:
@@ -4897,42 +4936,42 @@ gtk_clist_button_press (GtkWidget *widget,
gint row;
gint column;
gint button_actions;
-
+
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
-
+
clist = GTK_CLIST (widget);
-
+
button_actions = clist->button_actions[event->button - 1];
-
+
if (button_actions == GTK_BUTTON_IGNORED)
return FALSE;
-
+
/* selections on the list */
if (event->window == clist->clist_window)
{
x = event->x;
y = event->y;
-
+
if (get_selection_info (clist, x, y, &row, &column))
{
gint old_row = clist->focus_row;
-
+
if (clist->focus_row == -1)
old_row = row;
-
+
if (event->type == GDK_BUTTON_PRESS)
{
GdkEventMask mask = ((1 << (4 + event->button)) |
GDK_POINTER_MOTION_HINT_MASK |
GDK_BUTTON_RELEASE_MASK);
-
+
if (gdk_pointer_grab (clist->clist_window, FALSE, mask,
NULL, NULL, event->time))
return FALSE;
gtk_grab_add (widget);
-
+
clist->click_cell.row = row;
clist->click_cell.column = column;
clist->drag_button = event->button;
@@ -4941,11 +4980,11 @@ gtk_clist_button_press (GtkWidget *widget,
{
clist->click_cell.row = -1;
clist->click_cell.column = -1;
-
+
clist->drag_button = 0;
remove_grab (clist);
}
-
+
if (button_actions & GTK_BUTTON_SELECTS)
{
if (GTK_CLIST_ADD_MODE(clist))
@@ -4978,10 +5017,10 @@ gtk_clist_button_press (GtkWidget *widget,
clist->focus_row = row;
}
}
-
+
if (!GTK_WIDGET_HAS_FOCUS(widget))
gtk_widget_grab_focus (widget);
-
+
if (button_actions & GTK_BUTTON_SELECTS)
{
switch (clist->selection_mode)
@@ -5017,7 +5056,7 @@ gtk_clist_button_press (GtkWidget *widget,
row, column, event);
break;
}
-
+
if (event->state & GDK_CONTROL_MASK)
{
if (event->state & GDK_SHIFT_MASK)
@@ -5044,14 +5083,14 @@ gtk_clist_button_press (GtkWidget *widget,
}
break;
}
-
+
if (event->state & GDK_SHIFT_MASK)
{
set_anchor (clist, FALSE, old_row, old_row);
update_extended_selection (clist, clist->focus_row);
break;
}
-
+
if (clist->anchor == -1)
set_anchor (clist, FALSE, row, old_row);
else
@@ -5064,36 +5103,36 @@ gtk_clist_button_press (GtkWidget *widget,
}
return FALSE;
}
-
+
/* press on resize windows */
for (i = 0; i < clist->columns; i++)
if (clist->column[i].resizeable && clist->column[i].window &&
event->window == clist->column[i].window)
{
gpointer drag_data;
-
+
if (gdk_pointer_grab (clist->column[i].window, FALSE,
GDK_POINTER_MOTION_HINT_MASK |
GDK_BUTTON1_MOTION_MASK |
GDK_BUTTON_RELEASE_MASK,
NULL, NULL, event->time))
return FALSE;
-
+
gtk_grab_add (widget);
GTK_CLIST_SET_FLAG (clist, CLIST_IN_DRAG);
-
+
/* block attached dnd signal handler */
drag_data = gtk_object_get_data (GTK_OBJECT (clist), "gtk-site-data");
if (drag_data)
gtk_signal_handler_block_by_data (GTK_OBJECT (clist), drag_data);
-
+
if (!GTK_WIDGET_HAS_FOCUS(widget))
gtk_widget_grab_focus (widget);
-
+
clist->drag_pos = i;
clist->x_drag = (COLUMN_LEFT_XPIXEL(clist, i) + COLUMN_INSET +
clist->column[i].area.width + CELL_SPACING);
-
+
if (GTK_CLIST_ADD_MODE(clist))
gdk_gc_set_line_attributes (clist->xor_gc, 1, GDK_LINE_SOLID, 0, 0);
draw_xor_line (clist);
@@ -5107,17 +5146,17 @@ gtk_clist_button_release (GtkWidget *widget,
{
GtkCList *clist;
gint button_actions;
-
+
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
-
+
clist = GTK_CLIST (widget);
-
+
button_actions = clist->button_actions[event->button - 1];
if (button_actions == GTK_BUTTON_IGNORED)
return FALSE;
-
+
/* release on resize windows */
if (GTK_CLIST_IN_DRAG(clist))
{
@@ -5125,46 +5164,46 @@ gtk_clist_button_release (GtkWidget *widget,
gint width;
gint x;
gint i;
-
+
i = clist->drag_pos;
clist->drag_pos = -1;
-
+
/* unblock attached dnd signal handler */
drag_data = gtk_object_get_data (GTK_OBJECT (clist), "gtk-site-data");
if (drag_data)
gtk_signal_handler_unblock_by_data (GTK_OBJECT (clist), drag_data);
-
+
GTK_CLIST_UNSET_FLAG (clist, CLIST_IN_DRAG);
gtk_widget_get_pointer (widget, &x, NULL);
gtk_grab_remove (widget);
gdk_pointer_ungrab (event->time);
-
+
if (clist->x_drag >= 0)
draw_xor_line (clist);
-
+
if (GTK_CLIST_ADD_MODE(clist))
{
gdk_gc_set_line_attributes (clist->xor_gc, 1,
GDK_LINE_ON_OFF_DASH, 0, 0);
gdk_gc_set_dashes (clist->xor_gc, 0, "\4\4", 2);
}
-
+
width = new_column_width (clist, i, &x);
gtk_clist_set_column_width (clist, i, width);
return FALSE;
}
-
+
if (clist->drag_button == event->button)
{
gint row;
gint column;
-
+
clist->drag_button = 0;
clist->click_cell.row = -1;
clist->click_cell.column = -1;
-
+
remove_grab (clist);
-
+
if (button_actions & GTK_BUTTON_SELECTS)
{
switch (clist->selection_mode)
@@ -5205,17 +5244,17 @@ gtk_clist_motion (GtkWidget *widget,
gint row;
gint new_width;
gint button_actions = 0;
-
+
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
-
+
clist = GTK_CLIST (widget);
if (!(gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist)))
return FALSE;
-
+
if (clist->drag_button > 0)
button_actions = clist->button_actions[clist->drag_button - 1];
-
+
if (GTK_CLIST_IN_DRAG(clist))
{
if (event->is_hint || event->window != widget->window)
@@ -5229,13 +5268,13 @@ gtk_clist_motion (GtkWidget *widget,
/* x_drag < 0 indicates that the xor line is already invisible */
if (clist->x_drag >= 0)
draw_xor_line (clist);
-
+
clist->x_drag = x;
-
+
if (clist->x_drag >= 0)
draw_xor_line (clist);
}
-
+
if (new_width <= MAX (COLUMN_MIN_WIDTH + 1,
clist->column[clist->drag_pos].min_width + 1))
{
@@ -5255,7 +5294,7 @@ gtk_clist_motion (GtkWidget *widget,
return FALSE;
}
}
-
+
if (event->is_hint || event->window != clist->clist_window)
gdk_window_get_pointer (clist->clist_window, &x, &y, NULL);
else
@@ -5263,7 +5302,7 @@ gtk_clist_motion (GtkWidget *widget,
x = event->x;
y = event->y;
}
-
+
if (GTK_CLIST_REORDERABLE(clist) && button_actions & GTK_BUTTON_DRAGS)
{
/* delayed drag start */
@@ -5279,25 +5318,25 @@ gtk_clist_motion (GtkWidget *widget,
clist->column[clist->click_cell.column].area.width)))
{
GtkTargetList *target_list;
-
+
target_list = gtk_target_list_new (&clist_target_table, 1);
gtk_drag_begin (widget, target_list, GDK_ACTION_MOVE,
clist->drag_button, (GdkEvent *)event);
-
+
}
return TRUE;
}
-
+
/* horizontal autoscrolling */
if (clist->hadjustment && LIST_WIDTH (clist) > clist->clist_window_width &&
(x < 0 || x >= clist->clist_window_width))
{
if (clist->htimer)
return FALSE;
-
+
clist->htimer = gtk_timeout_add
(SCROLL_TIME, (GtkFunction) horizontal_timeout, clist);
-
+
if (!((x < 0 && clist->hadjustment->value == 0) ||
(x >= clist->clist_window_width &&
clist->hadjustment->value ==
@@ -5309,46 +5348,46 @@ gtk_clist_motion (GtkWidget *widget,
move_horizontal (clist, 1 + (x - clist->clist_window_width) / 2);
}
}
-
+
if (GTK_CLIST_IN_DRAG(clist))
return FALSE;
-
+
/* vertical autoscrolling */
row = ROW_FROM_YPIXEL (clist, y);
-
+
/* don't scroll on last pixel row if it's a cell spacing */
if (y == clist->clist_window_height - 1 &&
y == ROW_TOP_YPIXEL (clist, row-1) + clist->row_height)
return FALSE;
-
+
if (LIST_HEIGHT (clist) > clist->clist_window_height &&
(y < 0 || y >= clist->clist_window_height))
{
if (clist->vtimer)
return FALSE;
-
+
clist->vtimer = gtk_timeout_add (SCROLL_TIME,
(GtkFunction) vertical_timeout, clist);
-
+
if (clist->drag_button &&
((y < 0 && clist->focus_row == 0) ||
(y >= clist->clist_window_height &&
clist->focus_row == clist->rows - 1)))
return FALSE;
}
-
+
row = CLAMP (row, 0, clist->rows - 1);
-
+
if (button_actions & GTK_BUTTON_SELECTS &
!gtk_object_get_data (GTK_OBJECT (widget), "gtk-site-data"))
{
if (row == clist->focus_row)
return FALSE;
-
+
gtk_clist_draw_focus (widget);
clist->focus_row = row;
gtk_clist_draw_focus (widget);
-
+
switch (clist->selection_mode)
{
case GTK_SELECTION_BROWSE:
@@ -5368,7 +5407,7 @@ gtk_clist_motion (GtkWidget *widget,
else if (ROW_TOP_YPIXEL(clist, row) + clist->row_height >
clist->clist_window_height)
move_vertical (clist, row, 1);
-
+
return FALSE;
}
@@ -5378,16 +5417,16 @@ gtk_clist_size_request (GtkWidget *widget,
{
GtkCList *clist;
gint i;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CLIST (widget));
g_return_if_fail (requisition != NULL);
-
+
clist = GTK_CLIST (widget);
-
+
requisition->width = 0;
requisition->height = 0;
-
+
/* compute the size of the column title (title) area */
clist->column_title_area.height = 0;
if (GTK_CLIST_SHOW_TITLES(clist))
@@ -5402,13 +5441,13 @@ gtk_clist_size_request (GtkWidget *widget,
MAX (clist->column_title_area.height,
child_requisition.height);
}
-
+
requisition->width += (widget->style->klass->xthickness +
GTK_CONTAINER (widget)->border_width) * 2;
requisition->height += (clist->column_title_area.height +
(widget->style->klass->ythickness +
GTK_CONTAINER (widget)->border_width) * 2);
-
+
/* if (!clist->hadjustment) */
requisition->width += list_requisition_width (clist);
/* if (!clist->vadjustment) */
@@ -5422,15 +5461,15 @@ gtk_clist_size_allocate (GtkWidget *widget,
GtkCList *clist;
GtkAllocation clist_allocation;
gint border_width;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CLIST (widget));
g_return_if_fail (allocation != NULL);
-
+
clist = GTK_CLIST (widget);
widget->allocation = *allocation;
border_width = GTK_CONTAINER (widget)->border_width;
-
+
if (GTK_WIDGET_REALIZED (widget))
{
gdk_window_move_resize (widget->window,
@@ -5439,7 +5478,7 @@ gtk_clist_size_allocate (GtkWidget *widget,
allocation->width - border_width * 2,
allocation->height - border_width * 2);
}
-
+
/* use internal allocation structure for all the math
* because it's easier than always subtracting the container
* border width */
@@ -5449,7 +5488,7 @@ gtk_clist_size_allocate (GtkWidget *widget,
border_width * 2);
clist->internal_allocation.height = MAX (1, (gint)allocation->height -
border_width * 2);
-
+
/* allocate clist window assuming no scrollbars */
clist_allocation.x = (clist->internal_allocation.x +
widget->style->klass->xthickness);
@@ -5491,7 +5530,7 @@ gtk_clist_size_allocate (GtkWidget *widget,
/* column button allocation */
size_allocate_columns (clist, FALSE);
size_allocate_title_buttons (clist);
-
+
adjust_adjustments (clist, TRUE);
}
@@ -5506,16 +5545,16 @@ gtk_clist_forall (GtkContainer *container,
{
GtkCList *clist;
guint i;
-
+
g_return_if_fail (container != NULL);
g_return_if_fail (GTK_IS_CLIST (container));
g_return_if_fail (callback != NULL);
-
+
if (!include_internals)
return;
-
+
clist = GTK_CLIST (container);
-
+
/* callback for the column buttons */
for (i = 0; i < clist->columns; i++)
if (clist->column[i].button)
@@ -5540,13 +5579,13 @@ get_cell_style (GtkCList *clist,
GdkGC **bg_gc)
{
gint fg_state;
-
+
if ((state == GTK_STATE_NORMAL) &&
(GTK_WIDGET (clist)->state == GTK_STATE_INSENSITIVE))
fg_state = GTK_STATE_INSENSITIVE;
else
fg_state = state;
-
+
if (clist_row->cell[column].style)
{
if (style)
@@ -5585,7 +5624,7 @@ get_cell_style (GtkCList *clist,
else
*bg_gc = GTK_WIDGET (clist)->style->base_gc[state];
}
-
+
if (state != GTK_STATE_SELECTED)
{
if (fg_gc && clist_row->fg_set)
@@ -5609,13 +5648,13 @@ draw_cell_pixmap (GdkWindow *window,
{
gint xsrc = 0;
gint ysrc = 0;
-
+
if (mask)
{
gdk_gc_set_clip_mask (fg_gc, mask);
gdk_gc_set_clip_origin (fg_gc, x, y);
}
-
+
if (x < clip_rectangle->x)
{
xsrc = clip_rectangle->x - x;
@@ -5624,7 +5663,7 @@ draw_cell_pixmap (GdkWindow *window,
}
if (x + width > clip_rectangle->x + clip_rectangle->width)
width = clip_rectangle->x + clip_rectangle->width - x;
-
+
if (y < clip_rectangle->y)
{
ysrc = clip_rectangle->y - y;
@@ -5633,12 +5672,12 @@ draw_cell_pixmap (GdkWindow *window,
}
if (y + height > clip_rectangle->y + clip_rectangle->height)
height = clip_rectangle->y + clip_rectangle->height - y;
-
+
gdk_draw_pixmap (window, fg_gc, pixmap, xsrc, ysrc, x, y, width, height);
gdk_gc_set_clip_origin (fg_gc, 0, 0);
if (mask)
gdk_gc_set_clip_mask (fg_gc, NULL);
-
+
return x + MAX (width, 0);
}
@@ -5657,39 +5696,39 @@ draw_row (GtkCList *clist,
gint last_column;
gint state;
gint i;
-
+
g_return_if_fail (clist != NULL);
-
+
/* bail now if we arn't drawable yet */
if (!GTK_WIDGET_DRAWABLE (clist) || row < 0 || row >= clist->rows)
return;
-
+
widget = GTK_WIDGET (clist);
-
+
/* if the function is passed the pointer to the row instead of null,
* it avoids this expensive lookup */
if (!clist_row)
clist_row = ROW_ELEMENT (clist, row)->data;
-
+
/* rectangle of the entire row */
row_rectangle.x = 0;
row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
row_rectangle.width = clist->clist_window_width;
row_rectangle.height = clist->row_height;
-
+
/* rectangle of the cell spacing above the row */
cell_rectangle.x = 0;
cell_rectangle.y = row_rectangle.y - CELL_SPACING;
cell_rectangle.width = row_rectangle.width;
cell_rectangle.height = CELL_SPACING;
-
+
/* rectangle used to clip drawing operations, its y and height
* positions only need to be set once, so we set them once here.
* the x and width are set withing the drawing loop below once per
* column */
clip_rectangle.y = row_rectangle.y;
clip_rectangle.height = row_rectangle.height;
-
+
if (clist_row->state == GTK_STATE_NORMAL)
{
if (clist_row->fg_set)
@@ -5697,9 +5736,9 @@ draw_row (GtkCList *clist,
if (clist_row->bg_set)
gdk_gc_set_foreground (clist->bg_gc, &clist_row->background);
}
-
+
state = clist_row->state;
-
+
/* draw the cell borders and background */
if (area)
{
@@ -5713,12 +5752,12 @@ draw_row (GtkCList *clist,
intersect_rectangle.y,
intersect_rectangle.width,
intersect_rectangle.height);
-
+
/* the last row has to clear its bottom cell spacing too */
if (clist_row == clist->row_list_end->data)
{
cell_rectangle.y += clist->row_height + CELL_SPACING;
-
+
if (gdk_rectangle_intersect (area, &cell_rectangle,
&intersect_rectangle))
gdk_draw_rectangle (clist->clist_window,
@@ -5729,10 +5768,10 @@ draw_row (GtkCList *clist,
intersect_rectangle.width,
intersect_rectangle.height);
}
-
+
if (!gdk_rectangle_intersect (area, &row_rectangle,&intersect_rectangle))
return;
-
+
}
else
{
@@ -5744,12 +5783,12 @@ draw_row (GtkCList *clist,
cell_rectangle.y,
cell_rectangle.width,
cell_rectangle.height);
-
+
/* the last row has to clear its bottom cell spacing too */
if (clist_row == clist->row_list_end->data)
{
cell_rectangle.y += clist->row_height + CELL_SPACING;
-
+
gdk_draw_rectangle (clist->clist_window,
widget->style->base_gc[GTK_STATE_ACTIVE],
TRUE,
@@ -5763,28 +5802,29 @@ draw_row (GtkCList *clist,
for (last_column = clist->columns - 1;
last_column >= 0 && !clist->column[last_column].visible; last_column--)
;
-
+
/* iterate and draw all the columns (row cells) and draw their contents */
for (i = 0; i < clist->columns; i++)
{
GtkStyle *style;
GdkGC *fg_gc;
GdkGC *bg_gc;
-
+ PangoLayout *layout;
+ PangoRectangle logical_rect;
+
gint width;
gint height;
gint pixmap_width;
gint offset = 0;
- gint row_center_offset;
-
+
if (!clist->column[i].visible)
continue;
-
+
get_cell_style (clist, clist_row, state, i, &style, &fg_gc, &bg_gc);
-
+
clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
clip_rectangle.width = clist->column[i].area.width;
-
+
/* calculate clipping region clipping region */
clip_rectangle.x -= COLUMN_INSET + CELL_SPACING;
clip_rectangle.width += (2 * COLUMN_INSET + CELL_SPACING +
@@ -5793,42 +5833,44 @@ draw_row (GtkCList *clist,
if (area && !gdk_rectangle_intersect (area, &clip_rectangle,
&intersect_rectangle))
continue;
-
+
gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
rect->x, rect->y, rect->width, rect->height);
-
+
clip_rectangle.x += COLUMN_INSET + CELL_SPACING;
clip_rectangle.width -= (2 * COLUMN_INSET + CELL_SPACING +
(i == last_column) * CELL_SPACING);
-
+
+
/* calculate real width for column justification */
+
+ layout = _gtk_clist_create_cell_layout (clist, clist_row, i);
+ if (layout)
+ {
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+ width = logical_rect.width / PANGO_SCALE;
+ }
+ else
+ width = 0;
+
pixmap_width = 0;
offset = 0;
switch (clist_row->cell[i].type)
{
- case GTK_CELL_TEXT:
- width = gdk_string_width (style->font,
- GTK_CELL_TEXT (clist_row->cell[i])->text);
- break;
case GTK_CELL_PIXMAP:
gdk_window_get_size (GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap,
&pixmap_width, &height);
- width = pixmap_width;
+ width += pixmap_width;
break;
case GTK_CELL_PIXTEXT:
gdk_window_get_size (GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
&pixmap_width, &height);
- width = (pixmap_width +
- GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing +
- gdk_string_width (style->font,
- GTK_CELL_PIXTEXT
- (clist_row->cell[i])->text));
+ width += pixmap_width + GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
break;
default:
- continue;
break;
}
-
+
switch (clist->column[i].justification)
{
case GTK_JUSTIFY_LEFT:
@@ -5844,7 +5886,7 @@ draw_row (GtkCList *clist,
(clip_rectangle.width / 2) - (width / 2));
break;
};
-
+
/* Draw Text and/or Pixmap */
switch (clist_row->cell[i].type)
{
@@ -5867,29 +5909,27 @@ draw_row (GtkCList *clist,
(clip_rectangle.height - height) / 2,
pixmap_width, height);
offset += GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
+
+ /* Fall through */
case GTK_CELL_TEXT:
- if (style != GTK_WIDGET (clist)->style)
- row_center_offset = (((clist->row_height - style->font->ascent -
- style->font->descent - 1) / 2) + 1.5 +
- style->font->ascent);
- else
- row_center_offset = clist->row_center_offset;
-
- gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
- gdk_draw_string (clist->clist_window, style->font, fg_gc,
- offset,
- row_rectangle.y + row_center_offset +
- clist_row->cell[i].vertical,
- (clist_row->cell[i].type == GTK_CELL_PIXTEXT) ?
- GTK_CELL_PIXTEXT (clist_row->cell[i])->text :
- GTK_CELL_TEXT (clist_row->cell[i])->text);
- gdk_gc_set_clip_rectangle (fg_gc, NULL);
+ if (layout)
+ {
+ gint row_center_offset = 1.5 + (clist->row_height - logical_rect.height / PANGO_SCALE - 1) / 2;
+
+ gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
+ gdk_draw_layout (clist->clist_window, fg_gc,
+ offset,
+ row_rectangle.y + row_center_offset + clist_row->cell[i].vertical,
+ layout);
+ pango_layout_unref (layout);
+ gdk_gc_set_clip_rectangle (fg_gc, NULL);
+ }
break;
default:
break;
}
}
-
+
/* draw focus rectangle */
if (clist->focus_row == row &&
GTK_WIDGET_CAN_FOCUS (widget) && GTK_WIDGET_HAS_FOCUS(widget))
@@ -5920,14 +5960,14 @@ draw_rows (GtkCList *clist,
gint i;
gint first_row;
gint last_row;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (clist->row_height == 0 ||
!GTK_WIDGET_DRAWABLE (clist))
return;
-
+
if (area)
{
first_row = ROW_FROM_YPIXEL (clist, area->y);
@@ -5938,28 +5978,28 @@ draw_rows (GtkCList *clist,
first_row = ROW_FROM_YPIXEL (clist, 0);
last_row = ROW_FROM_YPIXEL (clist, clist->clist_window_height);
}
-
+
/* this is a small special case which exposes the bottom cell line
* on the last row -- it might go away if I change the wall the cell
* spacings are drawn
*/
if (clist->rows == first_row)
first_row--;
-
+
list = ROW_ELEMENT (clist, first_row);
i = first_row;
while (list)
{
clist_row = list->data;
list = list->next;
-
+
if (i > last_row)
return;
-
+
GTK_CLIST_GET_CLASS (clist)->draw_row (clist, area, i, clist_row);
i++;
}
-
+
if (!area)
gdk_window_clear_area (clist->clist_window, 0,
ROW_TOP_YPIXEL (clist, i), 0, 0);
@@ -5969,11 +6009,11 @@ static void
draw_xor_line (GtkCList *clist)
{
GtkWidget *widget;
-
+
g_return_if_fail (clist != NULL);
-
+
widget = GTK_WIDGET (clist);
-
+
gdk_draw_line (widget->window, clist->xor_gc,
clist->x_drag,
widget->style->klass->ythickness,
@@ -6007,26 +6047,26 @@ get_selection_info (GtkCList *clist,
gint *column)
{
gint trow, tcol;
-
+
g_return_val_if_fail (clist != NULL, 0);
g_return_val_if_fail (GTK_IS_CLIST (clist), 0);
-
+
/* bounds checking, return false if the user clicked
* on a blank area */
trow = ROW_FROM_YPIXEL (clist, y);
if (trow >= clist->rows)
return 0;
-
+
if (row)
*row = trow;
-
+
tcol = COLUMN_FROM_XPIXEL (clist, x);
if (tcol >= clist->columns)
return 0;
-
+
if (column)
*column = tcol;
-
+
return 1;
}
@@ -6061,7 +6101,7 @@ adjust_adjustments (GtkCList *clist,
clist->vadjustment->step_increment = clist->row_height;
clist->vadjustment->lower = 0;
clist->vadjustment->upper = LIST_HEIGHT (clist);
-
+
if (clist->clist_window_height - clist->voffset > LIST_HEIGHT (clist) ||
(clist->voffset + (gint)clist->vadjustment->value) != 0)
{
@@ -6072,7 +6112,7 @@ adjust_adjustments (GtkCList *clist,
}
gtk_signal_emit_by_name (GTK_OBJECT (clist->vadjustment), "changed");
}
-
+
if (clist->hadjustment)
{
clist->hadjustment->page_size = clist->clist_window_width;
@@ -6080,7 +6120,7 @@ adjust_adjustments (GtkCList *clist,
clist->hadjustment->step_increment = 10;
clist->hadjustment->lower = 0;
clist->hadjustment->upper = LIST_WIDTH (clist);
-
+
if (clist->clist_window_width - clist->hoffset > LIST_WIDTH (clist) ||
(clist->hoffset + (gint)clist->hadjustment->value) != 0)
{
@@ -6091,15 +6131,15 @@ adjust_adjustments (GtkCList *clist,
}
gtk_signal_emit_by_name (GTK_OBJECT (clist->hadjustment), "changed");
}
-
+
if (!block_resize && (!clist->vadjustment || !clist->hadjustment))
{
GtkWidget *widget;
GtkRequisition requisition;
-
+
widget = GTK_WIDGET (clist);
gtk_widget_size_request (widget, &requisition);
-
+
if ((!clist->hadjustment &&
requisition.width != widget->allocation.width) ||
(!clist->vadjustment &&
@@ -6113,10 +6153,10 @@ vadjustment_changed (GtkAdjustment *adjustment,
gpointer data)
{
GtkCList *clist;
-
+
g_return_if_fail (adjustment != NULL);
g_return_if_fail (data != NULL);
-
+
clist = GTK_CLIST (data);
}
@@ -6125,10 +6165,10 @@ hadjustment_changed (GtkAdjustment *adjustment,
gpointer data)
{
GtkCList *clist;
-
+
g_return_if_fail (adjustment != NULL);
g_return_if_fail (data != NULL);
-
+
clist = GTK_CLIST (data);
}
@@ -6139,23 +6179,23 @@ vadjustment_value_changed (GtkAdjustment *adjustment,
GtkCList *clist;
GdkRectangle area;
gint diff, value;
-
+
g_return_if_fail (adjustment != NULL);
g_return_if_fail (data != NULL);
g_return_if_fail (GTK_IS_CLIST (data));
-
+
clist = GTK_CLIST (data);
-
+
if (!GTK_WIDGET_DRAWABLE (clist) || adjustment != clist->vadjustment)
return;
-
+
value = adjustment->value;
-
+
if (value > -clist->voffset)
{
/* scroll down */
diff = value + clist->voffset;
-
+
/* we have to re-draw the whole screen here... */
if (diff >= clist->clist_window_height)
{
@@ -6163,13 +6203,13 @@ vadjustment_value_changed (GtkAdjustment *adjustment,
draw_rows (clist, NULL);
return;
}
-
+
if ((diff != 0) && (diff != clist->clist_window_height))
gdk_window_copy_area (clist->clist_window, clist->fg_gc,
0, 0, clist->clist_window, 0, diff,
clist->clist_window_width,
clist->clist_window_height - diff);
-
+
area.x = 0;
area.y = clist->clist_window_height - diff;
area.width = clist->clist_window_width;
@@ -6179,7 +6219,7 @@ vadjustment_value_changed (GtkAdjustment *adjustment,
{
/* scroll up */
diff = -clist->voffset - value;
-
+
/* we have to re-draw the whole screen here... */
if (diff >= clist->clist_window_height)
{
@@ -6187,23 +6227,23 @@ vadjustment_value_changed (GtkAdjustment *adjustment,
draw_rows (clist, NULL);
return;
}
-
+
if ((diff != 0) && (diff != clist->clist_window_height))
gdk_window_copy_area (clist->clist_window, clist->fg_gc,
0, diff, clist->clist_window, 0, 0,
clist->clist_window_width,
clist->clist_window_height - diff);
-
+
area.x = 0;
area.y = 0;
area.width = clist->clist_window_width;
area.height = diff;
}
-
+
clist->voffset = -value;
if ((diff != 0) && (diff != clist->clist_window_height))
check_exposures (clist);
-
+
draw_rows (clist, &area);
}
@@ -6217,18 +6257,18 @@ hadjustment_value_changed (GtkAdjustment *adjustment,
gint y = 0;
gint diff = 0;
gint value;
-
+
g_return_if_fail (adjustment != NULL);
g_return_if_fail (data != NULL);
g_return_if_fail (GTK_IS_CLIST (data));
-
+
clist = GTK_CLIST (data);
-
+
if (!GTK_WIDGET_DRAWABLE (clist) || adjustment != clist->hadjustment)
return;
-
+
value = adjustment->value;
-
+
/* move the column buttons and resize windows */
for (i = 0; i < clist->columns; i++)
{
@@ -6250,7 +6290,7 @@ hadjustment_value_changed (GtkAdjustment *adjustment,
}
}
}
-
+
if (value > -clist->hoffset)
{
/* scroll right */
@@ -6264,12 +6304,12 @@ hadjustment_value_changed (GtkAdjustment *adjustment,
draw_rows (clist, NULL);
return;
}
-
+
if (GTK_WIDGET_CAN_FOCUS(clist) && GTK_WIDGET_HAS_FOCUS(clist) &&
!GTK_CLIST_CHILD_HAS_FOCUS(clist) && GTK_CLIST_ADD_MODE(clist))
{
y = ROW_TOP_YPIXEL (clist, clist->focus_row);
-
+
gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE, 0, y,
clist->clist_window_width - 1,
clist->row_height - 1);
@@ -6282,7 +6322,7 @@ hadjustment_value_changed (GtkAdjustment *adjustment,
0,
clist->clist_window_width - diff,
clist->clist_window_height);
-
+
area.x = clist->clist_window_width - diff;
}
else
@@ -6290,7 +6330,7 @@ hadjustment_value_changed (GtkAdjustment *adjustment,
/* scroll left */
if (!(diff = -clist->hoffset - value))
return;
-
+
clist->hoffset = -value;
/* we have to re-draw the whole screen here... */
@@ -6309,7 +6349,7 @@ hadjustment_value_changed (GtkAdjustment *adjustment,
clist->clist_window_width - 1,
clist->row_height - 1);
}
-
+
gdk_window_copy_area (clist->clist_window,
clist->fg_gc,
diff, 0,
@@ -6318,16 +6358,16 @@ hadjustment_value_changed (GtkAdjustment *adjustment,
0,
clist->clist_window_width - diff,
clist->clist_window_height);
-
+
area.x = 0;
}
-
+
area.y = 0;
area.width = diff;
area.height = clist->clist_window_height;
-
+
check_exposures (clist);
-
+
if (GTK_WIDGET_CAN_FOCUS(clist) && GTK_WIDGET_HAS_FOCUS(clist) &&
!GTK_CLIST_CHILD_HAS_FOCUS(clist))
{
@@ -6376,10 +6416,10 @@ static void
check_exposures (GtkCList *clist)
{
GdkEvent *event;
-
+
if (!GTK_WIDGET_REALIZED (clist))
return;
-
+
/* Make sure graphics expose events are processed before scrolling
* again */
while ((event = gdk_event_get_graphics_expose (clist->clist_window)) != NULL)
@@ -6409,9 +6449,9 @@ columns_new (GtkCList *clist)
{
GtkCListColumn *column;
gint i;
-
+
column = g_new (GtkCListColumn, clist->columns);
-
+
for (i = 0; i < clist->columns; i++)
{
column[i].area.x = 0;
@@ -6431,7 +6471,7 @@ columns_new (GtkCList *clist)
column[i].button_passive = FALSE;
column[i].justification = GTK_JUSTIFY_LEFT;
}
-
+
return column;
}
@@ -6442,7 +6482,7 @@ column_title_new (GtkCList *clist,
{
if (clist->column[column].title)
g_free (clist->column[column].title);
-
+
clist->column[column].title = g_strdup (title);
}
@@ -6450,11 +6490,11 @@ static void
columns_delete (GtkCList *clist)
{
gint i;
-
+
for (i = 0; i < clist->columns; i++)
if (clist->column[i].title)
g_free (clist->column[i].title);
-
+
g_free (clist->column);
}
@@ -6463,10 +6503,10 @@ row_new (GtkCList *clist)
{
int i;
GtkCListRow *clist_row;
-
+
clist_row = g_chunk_new (GtkCListRow, clist->row_mem_chunk);
clist_row->cell = g_chunk_new (GtkCell, clist->cell_mem_chunk);
-
+
for (i = 0; i < clist->columns; i++)
{
clist_row->cell[i].type = GTK_CELL_EMPTY;
@@ -6474,7 +6514,7 @@ row_new (GtkCList *clist)
clist_row->cell[i].horizontal = 0;
clist_row->cell[i].style = NULL;
}
-
+
clist_row->fg_set = FALSE;
clist_row->bg_set = FALSE;
clist_row->style = NULL;
@@ -6482,7 +6522,7 @@ row_new (GtkCList *clist)
clist_row->state = GTK_STATE_NORMAL;
clist_row->data = NULL;
clist_row->destroy = NULL;
-
+
return clist_row;
}
@@ -6491,7 +6531,7 @@ row_delete (GtkCList *clist,
GtkCListRow *clist_row)
{
gint i;
-
+
for (i = 0; i < clist->columns; i++)
{
GTK_CLIST_GET_CLASS (clist)->set_cell_contents
@@ -6503,17 +6543,17 @@ row_delete (GtkCList *clist,
gtk_style_unref (clist_row->cell[i].style);
}
}
-
+
if (clist_row->style)
{
if (GTK_WIDGET_REALIZED (clist))
gtk_style_detach (clist_row->style);
gtk_style_unref (clist_row->style);
}
-
+
if (clist_row->destroy)
clist_row->destroy (clist_row->data);
-
+
g_mem_chunk_free (clist->cell_mem_chunk, clist_row->cell);
g_mem_chunk_free (clist->row_mem_chunk, clist_row);
}
@@ -6533,17 +6573,17 @@ gtk_clist_focus (GtkContainer *container,
GtkCList *clist;
GtkWidget *focus_child;
gint old_row;
-
+
g_return_val_if_fail (container != NULL, FALSE);
g_return_val_if_fail (GTK_IS_CLIST (container), FALSE);
-
+
if (!GTK_WIDGET_IS_SENSITIVE (container))
return FALSE;
clist = GTK_CLIST (container);
focus_child = container->focus_child;
old_row = clist->focus_row;
-
+
switch (direction)
{
case GTK_DIR_LEFT:
@@ -6554,7 +6594,7 @@ gtk_clist_focus (GtkContainer *container,
return TRUE;
gtk_container_set_focus_child (container, NULL);
return FALSE;
- }
+ }
gtk_widget_grab_focus (GTK_WIDGET (container));
return TRUE;
case GTK_DIR_DOWN:
@@ -6562,7 +6602,7 @@ gtk_clist_focus (GtkContainer *container,
if (GTK_CLIST_CHILD_HAS_FOCUS(clist))
{
gboolean tf = FALSE;
-
+
if (((focus_child && direction == GTK_DIR_DOWN) ||
!(tf = title_focus (clist, GTK_DIR_TAB_FORWARD)))
&& clist->rows)
@@ -6570,7 +6610,7 @@ gtk_clist_focus (GtkContainer *container,
if (clist->focus_row < 0)
{
clist->focus_row = 0;
-
+
if ((clist->selection_mode == GTK_SELECTION_BROWSE ||
clist->selection_mode == GTK_SELECTION_EXTENDED) &&
!clist->selection)
@@ -6581,7 +6621,7 @@ gtk_clist_focus (GtkContainer *container,
gtk_widget_grab_focus (GTK_WIDGET (container));
return TRUE;
}
-
+
if (tf)
return TRUE;
}
@@ -6606,17 +6646,17 @@ gtk_clist_focus (GtkContainer *container,
gtk_widget_grab_focus (GTK_WIDGET (container));
return TRUE;
}
-
+
GTK_CLIST_SET_FLAG (clist, CLIST_CHILD_HAS_FOCUS);
-
+
if (title_focus (clist, direction))
return TRUE;
-
+
break;
default:
break;
}
-
+
gtk_container_set_focus_child (container, NULL);
return FALSE;
}
@@ -6625,13 +6665,13 @@ static void
gtk_clist_draw_focus (GtkWidget *widget)
{
GtkCList *clist;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CLIST (widget));
-
+
if (!GTK_WIDGET_DRAWABLE (widget) || !GTK_WIDGET_CAN_FOCUS (widget))
return;
-
+
clist = GTK_CLIST (widget);
if (clist->focus_row >= 0)
gdk_draw_rectangle (clist->clist_window, clist->xor_gc, FALSE,
@@ -6645,21 +6685,21 @@ gtk_clist_focus_in (GtkWidget *widget,
GdkEventFocus *event)
{
GtkCList *clist;
-
+
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
-
+
GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
GTK_CLIST_UNSET_FLAG (widget, CLIST_CHILD_HAS_FOCUS);
-
+
clist = GTK_CLIST (widget);
-
+
if (clist->selection_mode == GTK_SELECTION_BROWSE &&
clist->selection == NULL && clist->focus_row > -1)
{
GList *list;
-
+
list = g_list_nth (clist->row_list, clist->focus_row);
if (list && GTK_CLIST_ROW (list)->selectable)
gtk_signal_emit (GTK_OBJECT (clist), clist_signals[SELECT_ROW],
@@ -6669,7 +6709,7 @@ gtk_clist_focus_in (GtkWidget *widget,
}
else
gtk_widget_draw_focus (widget);
-
+
return FALSE;
}
@@ -6678,20 +6718,20 @@ gtk_clist_focus_out (GtkWidget *widget,
GdkEventFocus *event)
{
GtkCList *clist;
-
+
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
-
+
GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
GTK_CLIST_SET_FLAG (widget, CLIST_CHILD_HAS_FOCUS);
-
+
gtk_widget_draw_focus (widget);
clist = GTK_CLIST (widget);
-
+
GTK_CLIST_GET_CLASS (widget)->resync_selection (clist, (GdkEvent *) event);
-
+
return FALSE;
}
@@ -6701,13 +6741,13 @@ gtk_clist_set_focus_child (GtkContainer *container,
{
g_return_if_fail (container != NULL);
g_return_if_fail (GTK_IS_CLIST (container));
-
+
if (child)
{
g_return_if_fail (GTK_IS_WIDGET (child));
GTK_CLIST_SET_FLAG (container, CLIST_CHILD_HAS_FOCUS);
}
-
+
parent_class->set_focus_child (container, child);
}
@@ -6721,12 +6761,12 @@ title_focus (GtkCList *clist,
gint d = 1;
gint i = 0;
gint j;
-
+
if (!GTK_CLIST_SHOW_TITLES(clist))
return FALSE;
-
+
focus_child = GTK_CONTAINER (clist)->focus_child;
-
+
for (last_column = clist->columns - 1;
last_column >= 0 && !clist->column[last_column].visible; last_column--)
;
@@ -6763,7 +6803,7 @@ title_focus (GtkCList *clist,
}
break;
}
-
+
if (focus_child)
while (i < clist->columns)
{
@@ -6786,9 +6826,9 @@ title_focus (GtkCList *clist,
}
i++;
}
-
+
j = i;
-
+
if (!return_val)
while (j >= 0 && j < clist->columns)
{
@@ -6844,12 +6884,12 @@ move_focus_row (GtkCList *clist,
gfloat position)
{
GtkWidget *widget;
-
+
g_return_if_fail (clist != 0);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
widget = GTK_WIDGET (clist);
-
+
switch (scroll_type)
{
case GTK_SCROLL_STEP_BACKWARD:
@@ -6906,17 +6946,17 @@ scroll_horizontal (GtkCList *clist,
{
gint column = 0;
gint last_column;
-
+
g_return_if_fail (clist != 0);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
return;
-
+
for (last_column = clist->columns - 1;
last_column >= 0 && !clist->column[last_column].visible; last_column--)
;
-
+
switch (scroll_type)
{
case GTK_SCROLL_STEP_BACKWARD:
@@ -6943,17 +6983,17 @@ scroll_horizontal (GtkCList *clist,
{
gint vis_columns = 0;
gint i;
-
+
for (i = 0; i <= last_column; i++)
if (clist->column[i].visible)
vis_columns++;
-
+
column = position * vis_columns;
-
+
for (i = 0; i <= last_column && column > 0; i++)
if (clist->column[i].visible)
column--;
-
+
column = i;
}
else
@@ -6962,7 +7002,7 @@ scroll_horizontal (GtkCList *clist,
default:
break;
}
-
+
if (COLUMN_LEFT_XPIXEL (clist, column) < CELL_SPACING + COLUMN_INSET)
gtk_clist_moveto (clist, -1, column, 0, 0);
else if (COLUMN_LEFT_XPIXEL (clist, column) + CELL_SPACING + COLUMN_INSET - 1
@@ -6981,23 +7021,23 @@ scroll_vertical (GtkCList *clist,
gfloat position)
{
gint old_focus_row;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
return;
-
+
switch (clist->selection_mode)
{
case GTK_SELECTION_EXTENDED:
if (clist->anchor >= 0)
return;
case GTK_SELECTION_BROWSE:
-
+
old_focus_row = clist->focus_row;
move_focus_row (clist, scroll_type, position);
-
+
if (old_focus_row != clist->focus_row)
{
if (clist->selection_mode == GTK_SELECTION_BROWSE)
@@ -7009,7 +7049,7 @@ scroll_vertical (GtkCList *clist,
clist->undo_anchor = old_focus_row;
}
}
-
+
switch (gtk_clist_row_is_visible (clist, clist->focus_row))
{
case GTK_VISIBILITY_NONE:
@@ -7063,7 +7103,7 @@ scroll_vertical (GtkCList *clist,
break;
default:
move_focus_row (clist, scroll_type, position);
-
+
if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
clist->clist_window_height)
gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
@@ -7078,10 +7118,10 @@ move_horizontal (GtkCList *clist,
gint diff)
{
gfloat value;
-
+
if (!clist->hadjustment)
return;
-
+
value = CLAMP (clist->hadjustment->value + diff, 0.0,
clist->hadjustment->upper - clist->hadjustment->page_size);
gtk_adjustment_set_value(clist->hadjustment, value);
@@ -7093,17 +7133,17 @@ move_vertical (GtkCList *clist,
gfloat align)
{
gfloat value;
-
+
if (!clist->vadjustment)
return;
-
+
value = (ROW_TOP_YPIXEL (clist, row) - clist->voffset -
align * (clist->clist_window_height - clist->row_height) +
(2 * align - 1) * CELL_SPACING);
-
+
if (value + clist->vadjustment->page_size > clist->vadjustment->upper)
value = clist->vadjustment->upper - clist->vadjustment->page_size;
-
+
gtk_adjustment_set_value(clist->vadjustment, value);
}
@@ -7111,16 +7151,16 @@ static gint
horizontal_timeout (GtkCList *clist)
{
GdkEventMotion event = { 0 };
-
+
GDK_THREADS_ENTER ();
-
+
clist->htimer = 0;
-
+
event.type = GDK_MOTION_NOTIFY;
event.send_event = TRUE;
-
+
gtk_clist_motion (GTK_WIDGET (clist), &event);
-
+
GDK_THREADS_LEAVE ();
return FALSE;
@@ -7130,18 +7170,18 @@ static gint
vertical_timeout (GtkCList *clist)
{
GdkEventMotion event = { 0 };
-
+
GDK_THREADS_ENTER ();
-
+
clist->vtimer = 0;
-
+
event.type = GDK_MOTION_NOTIFY;
event.send_event = TRUE;
-
+
gtk_clist_motion (GTK_WIDGET (clist), &event);
-
+
GDK_THREADS_LEAVE ();
-
+
return FALSE;
}
@@ -7154,13 +7194,13 @@ remove_grab (GtkCList *clist)
if (gdk_pointer_is_grabbed ())
gdk_pointer_ungrab (GDK_CURRENT_TIME);
}
-
+
if (clist->htimer)
{
gtk_timeout_remove (clist->htimer);
clist->htimer = 0;
}
-
+
if (clist->vtimer)
{
gtk_timeout_remove (clist->vtimer);
@@ -7180,7 +7220,7 @@ gtk_clist_sort (GtkCList *clist)
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
GTK_CLIST_GET_CLASS (clist)->sort_list (clist);
}
@@ -7190,7 +7230,7 @@ gtk_clist_set_compare_func (GtkCList *clist,
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
clist->compare = (cmp_func) ? cmp_func : default_compare;
}
@@ -7226,10 +7266,10 @@ gtk_clist_set_sort_column (GtkCList *clist,
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (column < 0 || column >= clist->columns)
return;
-
+
clist->sort_column = column;
}
@@ -7246,10 +7286,10 @@ default_compare (GtkCList *clist,
{
char *text1 = NULL;
char *text2 = NULL;
-
+
GtkCListRow *row1 = (GtkCListRow *) ptr1;
GtkCListRow *row2 = (GtkCListRow *) ptr2;
-
+
switch (row1->cell[clist->sort_column].type)
{
case GTK_CELL_TEXT:
@@ -7261,7 +7301,7 @@ default_compare (GtkCList *clist,
default:
break;
}
-
+
switch (row2->cell[clist->sort_column].type)
{
case GTK_CELL_TEXT:
@@ -7273,13 +7313,13 @@ default_compare (GtkCList *clist,
default:
break;
}
-
+
if (!text2)
return (text1 != NULL);
-
+
if (!text1)
return -1;
-
+
return strcmp (text1, text2);
}
@@ -7289,18 +7329,18 @@ real_sort_list (GtkCList *clist)
GList *list;
GList *work;
gint i;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (clist->rows <= 1)
return;
-
+
if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (clist))
return;
-
+
gtk_clist_freeze (clist);
-
+
if (clist->anchor != -1 && clist->selection_mode == GTK_SELECTION_EXTENDED)
{
GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
@@ -7309,11 +7349,11 @@ real_sort_list (GtkCList *clist)
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
}
-
+
clist->row_list = gtk_clist_mergesort (clist, clist->row_list, clist->rows);
-
+
work = clist->selection;
-
+
for (i = 0, list = clist->row_list; i < clist->rows; i++, list = list->next)
{
if (GTK_CLIST_ROW (list)->state == GTK_STATE_SELECTED)
@@ -7325,7 +7365,7 @@ real_sort_list (GtkCList *clist)
if (i == clist->rows - 1)
clist->row_list_end = list;
}
-
+
gtk_clist_thaw (clist);
}
@@ -7337,9 +7377,9 @@ gtk_clist_merge (GtkCList *clist,
GList z = { 0 }; /* auxiliary node */
GList *c;
gint cmp;
-
+
c = &z;
-
+
while (a || b)
{
if (a && !b)
@@ -7379,7 +7419,7 @@ gtk_clist_merge (GtkCList *clist,
}
}
}
-
+
return z.next;
}
@@ -7390,7 +7430,7 @@ gtk_clist_mergesort (GtkCList *clist,
{
GList *half;
gint i;
-
+
if (num == 1)
{
return list;
@@ -7401,15 +7441,15 @@ gtk_clist_mergesort (GtkCList *clist,
half = list;
for (i = 0; i < num / 2; i++)
half = half->next;
-
+
/* cut the list in two */
half->prev->next = NULL;
half->prev = NULL;
-
+
/* recursively sort both lists */
return gtk_clist_merge (clist,
- gtk_clist_mergesort (clist, list, num / 2),
- gtk_clist_mergesort (clist, half, num - num / 2));
+ gtk_clist_mergesort (clist, list, num / 2),
+ gtk_clist_mergesort (clist, half, num - num / 2));
}
}
@@ -7419,7 +7459,7 @@ static void
drag_source_info_destroy (gpointer data)
{
GtkCListCellInfo *info = data;
-
+
g_free (info);
}
@@ -7427,7 +7467,7 @@ static void
drag_dest_info_destroy (gpointer data)
{
GtkCListDestInfo *info = data;
-
+
g_free (info);
}
@@ -7438,15 +7478,15 @@ drag_dest_cell (GtkCList *clist,
GtkCListDestInfo *dest_info)
{
GtkWidget *widget;
-
+
widget = GTK_WIDGET (clist);
-
+
dest_info->insert_pos = GTK_CLIST_DRAG_NONE;
-
+
y -= (GTK_CONTAINER (clist)->border_width +
widget->style->klass->ythickness +
clist->column_title_area.height);
-
+
dest_info->cell.row = ROW_FROM_YPIXEL (clist, y);
if (dest_info->cell.row >= clist->rows)
{
@@ -7455,15 +7495,15 @@ drag_dest_cell (GtkCList *clist,
}
if (dest_info->cell.row < -1)
dest_info->cell.row = -1;
-
+
x -= GTK_CONTAINER (widget)->border_width + widget->style->klass->xthickness;
dest_info->cell.column = COLUMN_FROM_XPIXEL (clist, x);
-
+
if (dest_info->cell.row >= 0)
{
gint y_delta;
gint h = 0;
-
+
y_delta = y - ROW_TOP_YPIXEL (clist, dest_info->cell.row);
if (GTK_CLIST_DRAW_DRAG_RECT(clist))
@@ -7476,7 +7516,7 @@ drag_dest_cell (GtkCList *clist,
dest_info->insert_pos = GTK_CLIST_DRAG_BEFORE;
h = clist->row_height / 2;
}
-
+
if (GTK_CLIST_DRAW_DRAG_LINE(clist))
{
if (y_delta < h)
@@ -7493,16 +7533,16 @@ gtk_clist_drag_begin (GtkWidget *widget,
{
GtkCList *clist;
GtkCListCellInfo *info;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CLIST (widget));
g_return_if_fail (context != NULL);
-
+
clist = GTK_CLIST (widget);
-
+
clist->drag_button = 0;
remove_grab (clist);
-
+
switch (clist->selection_mode)
{
case GTK_SELECTION_EXTENDED:
@@ -7515,24 +7555,24 @@ gtk_clist_drag_begin (GtkWidget *widget,
case GTK_SELECTION_BROWSE:
break;
}
-
+
info = g_dataset_get_data (context, "gtk-clist-drag-source");
-
+
if (!info)
{
info = g_new (GtkCListCellInfo, 1);
-
+
if (clist->click_cell.row < 0)
clist->click_cell.row = 0;
else if (clist->click_cell.row >= clist->rows)
clist->click_cell.row = clist->rows - 1;
info->row = clist->click_cell.row;
info->column = clist->click_cell.column;
-
+
g_dataset_set_data_full (context, "gtk-clist-drag-source", info,
drag_source_info_destroy);
}
-
+
if (GTK_CLIST_USE_DRAG_ICONS (clist))
gtk_drag_set_icon_default (context);
}
@@ -7542,13 +7582,13 @@ gtk_clist_drag_end (GtkWidget *widget,
GdkDragContext *context)
{
GtkCList *clist;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CLIST (widget));
g_return_if_fail (context != NULL);
-
+
clist = GTK_CLIST (widget);
-
+
clist->click_cell.row = -1;
clist->click_cell.column = -1;
}
@@ -7560,13 +7600,13 @@ gtk_clist_drag_leave (GtkWidget *widget,
{
GtkCList *clist;
GtkCListDestInfo *dest_info;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CLIST (widget));
g_return_if_fail (context != NULL);
-
+
clist = GTK_CLIST (widget);
-
+
dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
if (dest_info)
@@ -7577,7 +7617,7 @@ gtk_clist_drag_leave (GtkWidget *widget,
{
GList *list;
GdkAtom atom = gdk_atom_intern ("gtk-clist-drag-reorder", FALSE);
-
+
list = context->targets;
while (list)
{
@@ -7606,33 +7646,33 @@ gtk_clist_drag_motion (GtkWidget *widget,
GtkCList *clist;
GtkCListDestInfo new_info;
GtkCListDestInfo *dest_info;
-
+
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
-
+
clist = GTK_CLIST (widget);
-
+
dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
-
+
if (!dest_info)
{
dest_info = g_new (GtkCListDestInfo, 1);
-
+
dest_info->insert_pos = GTK_CLIST_DRAG_NONE;
dest_info->cell.row = -1;
dest_info->cell.column = -1;
-
+
g_dataset_set_data_full (context, "gtk-clist-drag-dest", dest_info,
drag_dest_info_destroy);
}
-
+
drag_dest_cell (clist, x, y, &new_info);
-
+
if (GTK_CLIST_REORDERABLE (clist))
{
GList *list;
GdkAtom atom = gdk_atom_intern ("gtk-clist-drag-reorder", FALSE);
-
+
list = context->targets;
while (list)
{
@@ -7640,7 +7680,7 @@ gtk_clist_drag_motion (GtkWidget *widget,
break;
list = list->next;
}
-
+
if (list)
{
if (gtk_drag_get_source_widget (context) != widget ||
@@ -7658,7 +7698,7 @@ gtk_clist_drag_motion (GtkWidget *widget,
}
return TRUE;
}
-
+
if (new_info.cell.row != dest_info->cell.row ||
(new_info.cell.row == dest_info->cell.row &&
dest_info->insert_pos != new_info.insert_pos))
@@ -7668,7 +7708,7 @@ gtk_clist_drag_motion (GtkWidget *widget,
(clist, g_list_nth (clist->row_list,
dest_info->cell.row)->data,
dest_info->cell.row, dest_info->insert_pos);
-
+
dest_info->insert_pos = new_info.insert_pos;
dest_info->cell.row = new_info.cell.row;
dest_info->cell.column = new_info.cell.column;
@@ -7677,13 +7717,13 @@ gtk_clist_drag_motion (GtkWidget *widget,
(clist, g_list_nth (clist->row_list,
dest_info->cell.row)->data,
dest_info->cell.row, dest_info->insert_pos);
-
+
gdk_drag_status (context, context->suggested_action, time);
}
return TRUE;
}
}
-
+
dest_info->insert_pos = new_info.insert_pos;
dest_info->cell.row = new_info.cell.row;
dest_info->cell.column = new_info.cell.column;
@@ -7700,13 +7740,13 @@ gtk_clist_drag_drop (GtkWidget *widget,
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_CLIST (widget), FALSE);
g_return_val_if_fail (context != NULL, FALSE);
-
+
if (GTK_CLIST_REORDERABLE (widget) &&
gtk_drag_get_source_widget (context) == widget)
{
GList *list;
GdkAtom atom = gdk_atom_intern ("gtk-clist-drag-reorder", FALSE);
-
+
list = context->targets;
while (list)
{
@@ -7728,14 +7768,14 @@ gtk_clist_drag_data_received (GtkWidget *widget,
guint time)
{
GtkCList *clist;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CLIST (widget));
g_return_if_fail (context != NULL);
g_return_if_fail (selection_data != NULL);
-
+
clist = GTK_CLIST (widget);
-
+
if (GTK_CLIST_REORDERABLE (clist) &&
gtk_drag_get_source_widget (context) == widget &&
selection_data->target ==
@@ -7744,21 +7784,21 @@ gtk_clist_drag_data_received (GtkWidget *widget,
selection_data->length == sizeof (GtkCListCellInfo))
{
GtkCListCellInfo *source_info;
-
+
source_info = (GtkCListCellInfo *)(selection_data->data);
if (source_info)
{
GtkCListDestInfo dest_info;
-
+
drag_dest_cell (clist, x, y, &dest_info);
-
+
if (dest_info.insert_pos == GTK_CLIST_DRAG_AFTER)
dest_info.cell.row++;
if (source_info->row < dest_info.cell.row)
dest_info.cell.row--;
if (dest_info.cell.row != source_info->row)
gtk_clist_row_move (clist, source_info->row, dest_info.cell.row);
-
+
g_dataset_remove_data (context, "gtk-clist-drag-dest");
}
}
@@ -7775,21 +7815,21 @@ gtk_clist_drag_data_get (GtkWidget *widget,
g_return_if_fail (GTK_IS_CLIST (widget));
g_return_if_fail (context != NULL);
g_return_if_fail (selection_data != NULL);
-
+
if (selection_data->target ==
gdk_atom_intern ("gtk-clist-drag-reorder", FALSE))
{
GtkCListCellInfo *info;
-
+
info = g_dataset_get_data (context, "gtk-clist-drag-source");
-
+
if (info)
{
GtkCListCellInfo ret_info;
-
+
ret_info.row = info->row;
ret_info.column = info->column;
-
+
gtk_selection_data_set (selection_data, selection_data->target,
GTK_TYPE_POINTER, (guchar *) &ret_info,
sizeof (GtkCListCellInfo));
@@ -7807,9 +7847,9 @@ draw_drag_highlight (GtkCList *clist,
GtkCListDragPos drag_pos)
{
gint y;
-
+
y = ROW_TOP_YPIXEL (clist, dest_row_number) - 1;
-
+
switch (drag_pos)
{
case GTK_CLIST_DRAG_NONE:
@@ -7832,15 +7872,15 @@ gtk_clist_set_reorderable (GtkCList *clist,
gboolean reorderable)
{
GtkWidget *widget;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if ((GTK_CLIST_REORDERABLE(clist) != 0) == reorderable)
return;
-
+
widget = GTK_WIDGET (clist);
-
+
if (reorderable)
{
GTK_CLIST_SET_FLAG (clist, CLIST_REORDERABLE);
@@ -7861,7 +7901,7 @@ gtk_clist_set_use_drag_icons (GtkCList *clist,
{
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CLIST (clist));
-
+
if (use_icons != 0)
GTK_CLIST_SET_FLAG (clist, CLIST_USE_DRAG_ICONS);
else
@@ -7883,9 +7923,9 @@ gtk_clist_set_button_actions (GtkCList *clist,
remove_grab (clist);
clist->drag_button = 0;
}
-
+
GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
-
+
clist->button_actions[button] = button_actions;
}
}
diff --git a/gtk/gtkclist.h b/gtk/gtkclist.h
index 321fea5dc..e495cf05d 100644
--- a/gtk/gtkclist.h
+++ b/gtk/gtkclist.h
@@ -162,7 +162,6 @@ struct _GtkCList
/* rows */
gint rows;
- gint row_center_offset;
gint row_height;
GList *row_list;
GList *row_list_end;
@@ -779,6 +778,11 @@ void gtk_clist_sort (GtkCList *clist);
void gtk_clist_set_auto_sort (GtkCList *clist,
gboolean auto_sort);
+/* Private function for clist, ctree */
+
+PangoLayout *_gtk_clist_create_cell_layout (GtkCList *clist,
+ GtkCListRow *clist_row,
+ gint column);
#ifdef __cplusplus
}
diff --git a/gtk/gtkctree.c b/gtk/gtkctree.c
index 79ebafed3..91db6ecd5 100644
--- a/gtk/gtkctree.c
+++ b/gtk/gtkctree.c
@@ -56,17 +56,17 @@ COLUMN_FROM_XPIXEL (GtkCList * clist,
gint x)
{
gint i, cx;
-
+
for (i = 0; i < clist->columns; i++)
if (clist->column[i].visible)
{
cx = clist->column[i].area.x + clist->hoffset;
-
+
if (x >= (cx - (COLUMN_INSET + CELL_SPACING)) &&
x <= (cx + clist->column[i].area.width + COLUMN_INSET))
return i;
}
-
+
/* no match */
return -1;
}
@@ -318,7 +318,7 @@ GtkType
gtk_ctree_get_type (void)
{
static GtkType ctree_type = 0;
-
+
if (!ctree_type)
{
static const GtkTypeInfo ctree_info =
@@ -332,10 +332,10 @@ gtk_ctree_get_type (void)
/* reserved_2 */ NULL,
(GtkClassInitFunc) NULL,
};
-
+
ctree_type = gtk_type_unique (GTK_TYPE_CLIST, &ctree_info);
}
-
+
return ctree_type;
}
@@ -346,15 +346,15 @@ gtk_ctree_class_init (GtkCTreeClass *klass)
GtkWidgetClass *widget_class;
GtkCListClass *clist_class;
GtkBindingSet *binding_set;
-
+
object_class = (GtkObjectClass *) klass;
widget_class = (GtkWidgetClass *) klass;
container_class = (GtkContainerClass *) klass;
clist_class = (GtkCListClass *) klass;
-
+
parent_class = gtk_type_class (GTK_TYPE_CLIST);
container_class = gtk_type_class (GTK_TYPE_CONTAINER);
-
+
gtk_object_add_arg_type ("GtkCTree::n_columns",
GTK_TYPE_UINT,
GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT_ONLY,
@@ -385,7 +385,7 @@ gtk_ctree_class_init (GtkCTreeClass *klass)
ARG_EXPANDER_STYLE);
object_class->set_arg = gtk_ctree_set_arg;
object_class->get_arg = gtk_ctree_get_arg;
-
+
ctree_signals[TREE_SELECT_ROW] =
gtk_signal_new ("tree_select_row",
GTK_RUN_FIRST,
@@ -431,15 +431,15 @@ gtk_ctree_class_init (GtkCTreeClass *klass)
gtk_marshal_NONE__ENUM,
GTK_TYPE_NONE, 1, GTK_TYPE_CTREE_EXPANSION_TYPE);
gtk_object_class_add_signals (object_class, ctree_signals, LAST_SIGNAL);
-
+
widget_class->realize = gtk_ctree_realize;
widget_class->unrealize = gtk_ctree_unrealize;
widget_class->button_press_event = gtk_ctree_button_press;
-
+
widget_class->drag_begin = gtk_ctree_drag_begin;
widget_class->drag_motion = gtk_ctree_drag_motion;
widget_class->drag_data_received = gtk_ctree_drag_data_received;
-
+
clist_class->select_row = real_select_row;
clist_class->unselect_row = real_unselect_row;
clist_class->row_move = real_row_move;
@@ -458,14 +458,14 @@ gtk_ctree_class_init (GtkCTreeClass *klass)
clist_class->sort_list = real_sort_list;
clist_class->set_cell_contents = set_cell_contents;
clist_class->cell_size_request = cell_size_request;
-
+
klass->tree_select_row = real_tree_select;
klass->tree_unselect_row = real_tree_unselect;
klass->tree_expand = real_tree_expand;
klass->tree_collapse = real_tree_collapse;
klass->tree_move = real_tree_move;
klass->change_focus_row_expansion = change_focus_row_expansion;
-
+
binding_set = gtk_binding_set_by_class (klass);
gtk_binding_entry_add_signal (binding_set,
'+', GDK_SHIFT_MASK,
@@ -514,9 +514,9 @@ gtk_ctree_set_arg (GtkObject *object,
guint arg_id)
{
GtkCTree *ctree;
-
+
ctree = GTK_CTREE (object);
-
+
switch (arg_id)
{
case ARG_N_COLUMNS: /* construct-only arg, only set when !GTK_CONSTRUCTED */
@@ -562,9 +562,9 @@ gtk_ctree_get_arg (GtkObject *object,
guint arg_id)
{
GtkCTree *ctree;
-
+
ctree = GTK_CTREE (object);
-
+
switch (arg_id)
{
case ARG_N_COLUMNS:
@@ -598,12 +598,12 @@ static void
gtk_ctree_init (GtkCTree *ctree)
{
GtkCList *clist;
-
+
GTK_CLIST_SET_FLAG (ctree, CLIST_DRAW_DRAG_RECT);
GTK_CLIST_SET_FLAG (ctree, CLIST_DRAW_DRAG_LINE);
-
+
clist = GTK_CLIST (ctree);
-
+
ctree->tree_indent = 20;
ctree->tree_spacing = 5;
ctree->tree_column = 0;
@@ -611,7 +611,7 @@ gtk_ctree_init (GtkCTree *ctree)
ctree->expander_style = GTK_CTREE_EXPANDER_SQUARE;
ctree->drag_compare = NULL;
ctree->show_stub = TRUE;
-
+
clist->button_actions[0] |= GTK_BUTTON_EXPANDS;
}
@@ -622,24 +622,24 @@ ctree_attach_styles (GtkCTree *ctree,
{
GtkCList *clist;
gint i;
-
+
clist = GTK_CLIST (ctree);
-
+
if (GTK_CTREE_ROW (node)->row.style)
GTK_CTREE_ROW (node)->row.style =
gtk_style_attach (GTK_CTREE_ROW (node)->row.style, clist->clist_window);
-
+
if (GTK_CTREE_ROW (node)->row.fg_set || GTK_CTREE_ROW (node)->row.bg_set)
{
GdkColormap *colormap;
-
+
colormap = gtk_widget_get_colormap (GTK_WIDGET (ctree));
if (GTK_CTREE_ROW (node)->row.fg_set)
gdk_color_alloc (colormap, &(GTK_CTREE_ROW (node)->row.foreground));
if (GTK_CTREE_ROW (node)->row.bg_set)
gdk_color_alloc (colormap, &(GTK_CTREE_ROW (node)->row.background));
}
-
+
for (i = 0; i < clist->columns; i++)
if (GTK_CTREE_ROW (node)->row.cell[i].style)
GTK_CTREE_ROW (node)->row.cell[i].style =
@@ -654,9 +654,9 @@ ctree_detach_styles (GtkCTree *ctree,
{
GtkCList *clist;
gint i;
-
+
clist = GTK_CLIST (ctree);
-
+
if (GTK_CTREE_ROW (node)->row.style)
gtk_style_detach (GTK_CTREE_ROW (node)->row.style);
for (i = 0; i < clist->columns; i++)
@@ -673,15 +673,15 @@ gtk_ctree_realize (GtkWidget *widget)
GtkCTreeNode *node;
GtkCTreeNode *child;
gint i;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CTREE (widget));
-
+
GTK_WIDGET_CLASS (parent_class)->realize (widget);
-
+
ctree = GTK_CTREE (widget);
clist = GTK_CLIST (widget);
-
+
node = GTK_CTREE_NODE (clist->row_list);
for (i = 0; i < clist->rows; i++)
{
@@ -691,7 +691,7 @@ gtk_ctree_realize (GtkWidget *widget)
gtk_ctree_pre_recursive (ctree, child, ctree_attach_styles, NULL);
node = GTK_CTREE_NODE_NEXT (node);
}
-
+
values.foreground = widget->style->fg[GTK_STATE_NORMAL];
values.background = widget->style->base[GTK_STATE_NORMAL];
values.subwindow_mode = GDK_INCLUDE_INFERIORS;
@@ -702,7 +702,7 @@ gtk_ctree_realize (GtkWidget *widget)
GDK_GC_BACKGROUND |
GDK_GC_SUBWINDOW |
GDK_GC_LINE_STYLE);
-
+
if (ctree->line_style == GTK_CTREE_LINES_DOTTED)
{
gdk_gc_set_line_attributes (ctree->lines_gc, 1,
@@ -716,21 +716,21 @@ gtk_ctree_unrealize (GtkWidget *widget)
{
GtkCTree *ctree;
GtkCList *clist;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CTREE (widget));
-
+
GTK_WIDGET_CLASS (parent_class)->unrealize (widget);
-
+
ctree = GTK_CTREE (widget);
clist = GTK_CLIST (widget);
-
+
if (GTK_WIDGET_REALIZED (widget))
{
GtkCTreeNode *node;
GtkCTreeNode *child;
gint i;
-
+
node = GTK_CTREE_NODE (clist->row_list);
for (i = 0; i < clist->rows; i++)
{
@@ -742,7 +742,7 @@ gtk_ctree_unrealize (GtkWidget *widget)
node = GTK_CTREE_NODE_NEXT (node);
}
}
-
+
gdk_gc_destroy (ctree->lines_gc);
}
@@ -753,19 +753,19 @@ gtk_ctree_button_press (GtkWidget *widget,
GtkCTree *ctree;
GtkCList *clist;
gint button_actions;
-
+
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_CTREE (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
-
+
ctree = GTK_CTREE (widget);
clist = GTK_CLIST (widget);
-
+
button_actions = clist->button_actions[event->button - 1];
-
+
if (button_actions == GTK_BUTTON_IGNORED)
return FALSE;
-
+
if (event->window == clist->clist_window)
{
GtkCTreeNode *work;
@@ -773,15 +773,15 @@ gtk_ctree_button_press (GtkWidget *widget,
gint y;
gint row;
gint column;
-
+
x = event->x;
y = event->y;
-
+
if (!gtk_clist_get_selection_info (clist, x, y, &row, &column))
return FALSE;
-
+
work = GTK_CTREE_NODE (g_list_nth (clist->row_list, row));
-
+
if (button_actions & GTK_BUTTON_EXPANDS &&
(GTK_CTREE_ROW (work)->children && !GTK_CTREE_ROW (work)->is_leaf &&
(event->type == GDK_2BUTTON_PRESS ||
@@ -791,7 +791,7 @@ gtk_ctree_button_press (GtkWidget *widget,
gtk_ctree_collapse (ctree, work);
else
gtk_ctree_expand (ctree, work);
-
+
return FALSE;
}
}
@@ -809,16 +809,16 @@ draw_drag_highlight (GtkCList *clist,
gint level;
gint i;
gint y = 0;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CTREE (clist));
-
+
ctree = GTK_CTREE (clist);
-
+
level = ((GtkCTreeRow *)(dest_row))->level;
-
+
y = ROW_TOP_YPIXEL (clist, dest_row_number) - 1;
-
+
switch (drag_pos)
{
case GTK_CLIST_DRAG_NONE:
@@ -839,7 +839,7 @@ draw_drag_highlight (GtkCList *clist,
COLUMN_LEFT_XPIXEL(clist, ctree->tree_column - 1)+
clist->column[ctree->tree_column - 1].area.width,
y);
-
+
gdk_draw_line (clist->clist_window, clist->xor_gc,
COLUMN_LEFT_XPIXEL(clist, ctree->tree_column) +
ctree->tree_indent * level -
@@ -853,7 +853,7 @@ draw_drag_highlight (GtkCList *clist,
y,
COLUMN_LEFT_XPIXEL(clist, clist->columns - 1) +
clist->column[clist->columns - 1].area.width, y);
-
+
gdk_draw_line (clist->clist_window, clist->xor_gc,
0, y, COLUMN_LEFT_XPIXEL(clist, ctree->tree_column)
+ clist->column[ctree->tree_column].area.width -
@@ -867,7 +867,7 @@ draw_drag_highlight (GtkCList *clist,
break;
case GTK_CLIST_DRAG_INTO:
y = ROW_TOP_YPIXEL (clist, dest_row_number) + clist->row_height;
-
+
if (clist->column[ctree->tree_column].visible)
switch (clist->column[ctree->tree_column].justification)
{
@@ -883,12 +883,12 @@ draw_drag_highlight (GtkCList *clist,
points[1].y = points[0].y;
points[2].x = points[1].x;
points[2].y = points[3].y;
-
+
for (i = 0; i < 3; i++)
gdk_draw_line (clist->clist_window, clist->xor_gc,
points[i].x, points[i].y,
points[i+1].x, points[i+1].y);
-
+
if (ctree->tree_column > 0)
{
points[0].x = COLUMN_LEFT_XPIXEL(clist,
@@ -901,7 +901,7 @@ draw_drag_highlight (GtkCList *clist,
points[1].y = points[0].y;
points[2].x = 0;
points[2].y = points[3].y;
-
+
for (i = 0; i < 3; i++)
gdk_draw_line (clist->clist_window, clist->xor_gc,
points[i].x, points[i].y, points[i+1].x,
@@ -919,12 +919,12 @@ draw_drag_highlight (GtkCList *clist,
points[1].y = points[0].y;
points[2].x = 0;
points[2].y = points[3].y;
-
+
for (i = 0; i < 3; i++)
gdk_draw_line (clist->clist_window, clist->xor_gc,
points[i].x, points[i].y,
points[i+1].x, points[i+1].y);
-
+
if (ctree->tree_column < clist->columns - 1)
{
points[0].x = COLUMN_LEFT_XPIXEL(clist, ctree->tree_column +1);
@@ -935,7 +935,7 @@ draw_drag_highlight (GtkCList *clist,
points[1].y = points[0].y;
points[2].x = points[1].x;
points[2].y = points[3].y;
-
+
for (i = 0; i < 3; i++)
gdk_draw_line (clist->clist_window, clist->xor_gc,
points[i].x, points[i].y,
@@ -964,7 +964,7 @@ draw_cell_pixmap (GdkWindow *window,
{
gint xsrc = 0;
gint ysrc = 0;
-
+
if (mask)
{
gdk_gc_set_clip_mask (fg_gc, mask);
@@ -978,7 +978,7 @@ draw_cell_pixmap (GdkWindow *window,
}
if (x + width > clip_rectangle->x + clip_rectangle->width)
width = clip_rectangle->x + clip_rectangle->width - x;
-
+
if (y < clip_rectangle->y)
{
ysrc = clip_rectangle->y - y;
@@ -987,16 +987,16 @@ draw_cell_pixmap (GdkWindow *window,
}
if (y + height > clip_rectangle->y + clip_rectangle->height)
height = clip_rectangle->y + clip_rectangle->height - y;
-
+
if (width > 0 && height > 0)
gdk_draw_pixmap (window, fg_gc, pixmap, xsrc, ysrc, x, y, width, height);
-
+
if (mask)
{
gdk_gc_set_clip_rectangle (fg_gc, NULL);
gdk_gc_set_clip_origin (fg_gc, 0, 0);
}
-
+
return x + MAX (width, 0);
}
@@ -1010,13 +1010,13 @@ get_cell_style (GtkCList *clist,
GdkGC **bg_gc)
{
gint fg_state;
-
+
if ((state == GTK_STATE_NORMAL) &&
(GTK_WIDGET (clist)->state == GTK_STATE_INSENSITIVE))
fg_state = GTK_STATE_INSENSITIVE;
else
fg_state = state;
-
+
if (clist_row->cell[column].style)
{
if (style)
@@ -1055,7 +1055,7 @@ get_cell_style (GtkCList *clist,
else
*bg_gc = GTK_WIDGET (clist)->style->base_gc[state];
}
-
+
if (state != GTK_STATE_SELECTED)
{
if (fg_gc && clist_row->fg_set)
@@ -1077,10 +1077,10 @@ gtk_ctree_draw_expander (GtkCTree *ctree,
GdkPoint points[3];
gint justification_factor;
gint y;
-
- if (ctree->expander_style == GTK_CTREE_EXPANDER_NONE)
- return x;
-
+
+ if (ctree->expander_style == GTK_CTREE_EXPANDER_NONE)
+ return x;
+
clist = GTK_CLIST (ctree);
if (clist->column[ctree->tree_column].justification == GTK_JUSTIFY_RIGHT)
justification_factor = -1;
@@ -1088,7 +1088,7 @@ gtk_ctree_draw_expander (GtkCTree *ctree,
justification_factor = 1;
y = (clip_rectangle->y + (clip_rectangle->height - PM_SIZE) / 2 -
(clip_rectangle->height + 1) % 2);
-
+
if (!ctree_row->children)
{
switch (ctree->expander_style)
@@ -1102,10 +1102,10 @@ gtk_ctree_draw_expander (GtkCTree *ctree,
return x + justification_factor * (PM_SIZE + 1);
}
}
-
+
gdk_gc_set_clip_rectangle (style->fg_gc[GTK_STATE_NORMAL], clip_rectangle);
gdk_gc_set_clip_rectangle (style->base_gc[GTK_STATE_NORMAL], clip_rectangle);
-
+
switch (ctree->expander_style)
{
case GTK_CTREE_EXPANDER_NONE:
@@ -1131,19 +1131,19 @@ gtk_ctree_draw_expander (GtkCTree *ctree,
justification_factor * (2 * (PM_SIZE + 2) / 3 - 1));
points[2].y = points[0].y + (PM_SIZE + 2) / 2;
}
-
+
gdk_draw_polygon (clist->clist_window, style->base_gc[GTK_STATE_NORMAL],
TRUE, points, 3);
gdk_draw_polygon (clist->clist_window, style->fg_gc[GTK_STATE_NORMAL],
FALSE, points, 3);
-
+
x += justification_factor * (PM_SIZE + 3);
break;
case GTK_CTREE_EXPANDER_SQUARE:
case GTK_CTREE_EXPANDER_CIRCULAR:
if (justification_factor == -1)
x += justification_factor * (PM_SIZE + 1);
-
+
if (ctree->expander_style == GTK_CTREE_EXPANDER_CIRCULAR)
{
gdk_draw_arc (clist->clist_window, style->base_gc[GTK_STATE_NORMAL],
@@ -1160,23 +1160,23 @@ gtk_ctree_draw_expander (GtkCTree *ctree,
style->fg_gc[GTK_STATE_NORMAL], FALSE,
x, y, PM_SIZE, PM_SIZE);
}
-
+
gdk_draw_line (clist->clist_window, style->fg_gc[GTK_STATE_NORMAL],
x + 2, y + PM_SIZE / 2, x + PM_SIZE - 2, y + PM_SIZE / 2);
-
+
if (!ctree_row->expanded)
gdk_draw_line (clist->clist_window, style->fg_gc[GTK_STATE_NORMAL],
x + PM_SIZE / 2, y + 2,
x + PM_SIZE / 2, y + PM_SIZE - 2);
-
+
if (justification_factor == 1)
x += justification_factor * (PM_SIZE + 1);
break;
}
-
+
gdk_gc_set_clip_rectangle (style->fg_gc[GTK_STATE_NORMAL], NULL);
gdk_gc_set_clip_rectangle (style->base_gc[GTK_STATE_NORMAL], NULL);
-
+
return x;
}
@@ -1213,7 +1213,7 @@ gtk_ctree_draw_lines (GtkCTree *ctree,
clist = GTK_CLIST (ctree);
ycenter = clip_rectangle->y + (clip_rectangle->height / 2);
justify_right = (clist->column[column].justification == GTK_JUSTIFY_RIGHT);
-
+
if (justify_right)
{
offset = (clip_rectangle->x + clip_rectangle->width - 1 -
@@ -1225,25 +1225,25 @@ gtk_ctree_draw_lines (GtkCTree *ctree,
offset = clip_rectangle->x + ctree->tree_indent * (ctree_row->level - 1);
justification_factor = 1;
}
-
+
switch (ctree->line_style)
{
case GTK_CTREE_LINES_NONE:
break;
case GTK_CTREE_LINES_TABBED:
xcenter = offset + justification_factor * TAB_SIZE;
-
+
column_right = (COLUMN_LEFT_XPIXEL (clist, ctree->tree_column) +
clist->column[ctree->tree_column].area.width +
COLUMN_INSET);
column_left = (COLUMN_LEFT_XPIXEL (clist, ctree->tree_column) -
COLUMN_INSET - CELL_SPACING);
-
+
if (area)
{
tree_rectangle.y = crect->y;
tree_rectangle.height = crect->height;
-
+
if (justify_right)
{
tree_rectangle.x = xcenter;
@@ -1254,18 +1254,18 @@ gtk_ctree_draw_lines (GtkCTree *ctree,
tree_rectangle.x = column_left;
tree_rectangle.width = xcenter - column_left;
}
-
+
if (!gdk_rectangle_intersect (area, &tree_rectangle, &tc_rectangle))
{
offset += justification_factor * 3;
break;
}
}
-
+
gdk_gc_set_clip_rectangle (ctree->lines_gc, crect);
-
+
next_level = ctree_row->level;
-
+
if (!ctree_row->sibling || (ctree_row->children && ctree_row->expanded))
{
node = gtk_ctree_find_node_ptr (ctree, ctree_row);
@@ -1274,21 +1274,21 @@ gtk_ctree_draw_lines (GtkCTree *ctree,
else
next_level = 0;
}
-
+
if (ctree->tree_indent > 0)
{
node = ctree_row->parent;
while (node)
{
xcenter -= (justification_factor * ctree->tree_indent);
-
+
if ((justify_right && xcenter < column_left) ||
(!justify_right && xcenter > column_right))
{
node = GTK_CTREE_ROW (node)->parent;
continue;
}
-
+
tree_rectangle.y = cell_rectangle->y;
tree_rectangle.height = cell_rectangle->height;
if (justify_right)
@@ -1304,17 +1304,17 @@ gtk_ctree_draw_lines (GtkCTree *ctree,
tree_rectangle.width = MIN (column_right - xcenter,
ctree->tree_indent);
}
-
+
if (!area || gdk_rectangle_intersect (area, &tree_rectangle,
&tc_rectangle))
{
get_cell_style (clist, &GTK_CTREE_ROW (node)->row,
state, column, NULL, NULL, &bg_gc);
-
+
if (bg_gc == clist->bg_gc)
gdk_gc_set_foreground
(clist->bg_gc, &GTK_CTREE_ROW (node)->row.background);
-
+
if (!area)
gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
tree_rectangle.x,
@@ -1335,16 +1335,16 @@ gtk_ctree_draw_lines (GtkCTree *ctree,
else
{
gint width;
-
+
offset_x = MIN (ctree->tree_indent, 2 * TAB_SIZE);
width = offset_x / 2 + offset_x % 2;
-
+
parent = GTK_CTREE_ROW (node)->parent;
-
+
tree_rectangle.y = ycenter;
tree_rectangle.height = (cell_rectangle->y - ycenter +
cell_rectangle->height);
-
+
if (justify_right)
{
tree_rectangle.x = MAX(xcenter + 1 - width, column_left);
@@ -1357,7 +1357,7 @@ gtk_ctree_draw_lines (GtkCTree *ctree,
tree_rectangle.width = MIN (column_right - xcenter,
width);
}
-
+
if (!area ||
gdk_rectangle_intersect (area, &tree_rectangle,
&tc_rectangle))
@@ -1375,7 +1375,7 @@ gtk_ctree_draw_lines (GtkCTree *ctree,
bg_gc = style->base_gc[state];
else
bg_gc = GTK_WIDGET (clist)->style->base_gc[state];
-
+
if (!area)
gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
tree_rectangle.x,
@@ -1390,13 +1390,13 @@ gtk_ctree_draw_lines (GtkCTree *ctree,
tc_rectangle.width,
tc_rectangle.height);
}
-
+
get_cell_style (clist, &GTK_CTREE_ROW (node)->row,
state, column, NULL, NULL, &bg_gc);
if (bg_gc == clist->bg_gc)
gdk_gc_set_foreground
(clist->bg_gc, &GTK_CTREE_ROW (node)->row.background);
-
+
gdk_gc_set_clip_rectangle (bg_gc, crect);
gdk_draw_arc (clist->clist_window, bg_gc, TRUE,
xcenter - (justify_right * offset_x),
@@ -1404,10 +1404,10 @@ gtk_ctree_draw_lines (GtkCTree *ctree,
offset_x, clist->row_height,
(180 + (justify_right * 90)) * 64, 90 * 64);
gdk_gc_set_clip_rectangle (bg_gc, NULL);
-
+
gdk_draw_line (clist->clist_window, ctree->lines_gc,
xcenter, cell_rectangle->y, xcenter, ycenter);
-
+
if (justify_right)
gdk_draw_arc (clist->clist_window, ctree->lines_gc, FALSE,
xcenter - offset_x, cell_rectangle->y,
@@ -1422,7 +1422,7 @@ gtk_ctree_draw_lines (GtkCTree *ctree,
node = GTK_CTREE_ROW (node)->parent;
}
}
-
+
if (state != GTK_STATE_SELECTED)
{
tree_rectangle.y = clip_rectangle->y;
@@ -1430,12 +1430,12 @@ gtk_ctree_draw_lines (GtkCTree *ctree,
tree_rectangle.width = COLUMN_INSET + CELL_SPACING +
MIN (clist->column[ctree->tree_column].area.width + COLUMN_INSET,
TAB_SIZE);
-
+
if (justify_right)
tree_rectangle.x = MAX (xcenter + 1, column_left);
else
tree_rectangle.x = column_left;
-
+
if (!area)
gdk_draw_rectangle (clist->clist_window,
GTK_WIDGET
@@ -1456,37 +1456,37 @@ gtk_ctree_draw_lines (GtkCTree *ctree,
tc_rectangle.width,
tc_rectangle.height);
}
-
+
xcenter = offset + (justification_factor * ctree->tree_indent / 2);
-
+
get_cell_style (clist, &ctree_row->row, state, column, NULL, NULL,
&bg_gc);
if (bg_gc == clist->bg_gc)
gdk_gc_set_foreground (clist->bg_gc, &ctree_row->row.background);
-
+
gdk_gc_set_clip_rectangle (bg_gc, crect);
if (ctree_row->is_leaf)
{
GdkPoint points[6];
-
+
points[0].x = offset + justification_factor * TAB_SIZE;
points[0].y = cell_rectangle->y;
-
+
points[1].x = points[0].x - justification_factor * 4;
points[1].y = points[0].y;
-
+
points[2].x = points[1].x - justification_factor * 2;
points[2].y = points[1].y + 3;
-
+
points[3].x = points[2].x;
points[3].y = points[2].y + clist->row_height - 5;
-
+
points[4].x = points[3].x + justification_factor * 2;
points[4].y = points[3].y + 3;
-
+
points[5].x = points[4].x + justification_factor * 4;
points[5].y = points[4].y;
-
+
gdk_draw_polygon (clist->clist_window, bg_gc, TRUE, points, 6);
gdk_draw_lines (clist->clist_window, ctree->lines_gc, points, 6);
}
@@ -1505,17 +1505,17 @@ gtk_ctree_draw_lines (GtkCTree *ctree,
}
gdk_gc_set_clip_rectangle (bg_gc, NULL);
gdk_gc_set_clip_rectangle (ctree->lines_gc, NULL);
-
+
offset += justification_factor * 3;
break;
default:
xcenter = offset + justification_factor * PM_SIZE / 2;
-
+
if (area)
{
tree_rectangle.y = crect->y;
tree_rectangle.height = crect->height;
-
+
if (justify_right)
{
tree_rectangle.x = xcenter - PM_SIZE / 2 - 2;
@@ -1528,11 +1528,11 @@ gtk_ctree_draw_lines (GtkCTree *ctree,
tree_rectangle.width = (xcenter + PM_SIZE / 2 + 2 -
clip_rectangle->x);
}
-
+
if (!gdk_rectangle_intersect (area, &tree_rectangle, &tc_rectangle))
break;
}
-
+
offset_x = 1;
offset_y = 0;
if (ctree->line_style == GTK_CTREE_LINES_DOTTED)
@@ -1540,7 +1540,7 @@ gtk_ctree_draw_lines (GtkCTree *ctree,
offset_x += abs((clip_rectangle->x + clist->hoffset) % 2);
offset_y = abs((cell_rectangle->y + clist->voffset) % 2);
}
-
+
clip_rectangle->y--;
clip_rectangle->height++;
gdk_gc_set_clip_rectangle (ctree->lines_gc, clip_rectangle);
@@ -1550,17 +1550,17 @@ gtk_ctree_draw_lines (GtkCTree *ctree,
cell_rectangle->y + offset_y : ycenter,
xcenter,
(ctree_row->sibling) ? crect->y +crect->height : ycenter);
-
+
gdk_draw_line (clist->clist_window, ctree->lines_gc,
xcenter + (justification_factor * offset_x), ycenter,
xcenter + (justification_factor * (PM_SIZE / 2 + 2)),
ycenter);
-
+
node = ctree_row->parent;
while (node)
{
xcenter -= (justification_factor * ctree->tree_indent);
-
+
if (GTK_CTREE_ROW (node)->sibling)
gdk_draw_line (clist->clist_window, ctree->lines_gc,
xcenter, cell_rectangle->y + offset_y,
@@ -1595,40 +1595,40 @@ draw_row (GtkCList *clist,
gint offset = 0;
gint state;
gint i;
-
+
g_return_if_fail (clist != NULL);
-
+
/* bail now if we arn't drawable yet */
if (!GTK_WIDGET_DRAWABLE (clist) || row < 0 || row >= clist->rows)
return;
-
+
widget = GTK_WIDGET (clist);
ctree = GTK_CTREE (clist);
-
+
/* if the function is passed the pointer to the row instead of null,
* it avoids this expensive lookup */
if (!clist_row)
clist_row = (g_list_nth (clist->row_list, row))->data;
-
+
/* rectangle of the entire row */
row_rectangle.x = 0;
row_rectangle.y = ROW_TOP_YPIXEL (clist, row);
row_rectangle.width = clist->clist_window_width;
row_rectangle.height = clist->row_height;
-
+
/* rectangle of the cell spacing above the row */
cell_rectangle.x = 0;
cell_rectangle.y = row_rectangle.y - CELL_SPACING;
cell_rectangle.width = row_rectangle.width;
cell_rectangle.height = CELL_SPACING;
-
+
/* rectangle used to clip drawing operations, its y and height
* positions only need to be set once, so we set them once here.
* the x and width are set withing the drawing loop below once per
* column */
clip_rectangle.y = row_rectangle.y;
clip_rectangle.height = row_rectangle.height;
-
+
if (clist_row->state == GTK_STATE_NORMAL)
{
if (clist_row->fg_set)
@@ -1638,16 +1638,16 @@ draw_row (GtkCList *clist,
}
state = clist_row->state;
-
+
gdk_gc_set_foreground (ctree->lines_gc,
&widget->style->fg[clist_row->state]);
-
+
/* draw the cell borders */
if (area)
{
rect = &intersect_rectangle;
crect = &intersect_rectangle;
-
+
if (gdk_rectangle_intersect (area, &cell_rectangle, crect))
gdk_draw_rectangle (clist->clist_window,
widget->style->base_gc[GTK_STATE_ACTIVE], TRUE,
@@ -1657,22 +1657,22 @@ draw_row (GtkCList *clist,
{
rect = &clip_rectangle;
crect = &cell_rectangle;
-
+
gdk_draw_rectangle (clist->clist_window,
widget->style->base_gc[GTK_STATE_ACTIVE], TRUE,
crect->x, crect->y, crect->width, crect->height);
}
-
+
/* horizontal black lines */
if (ctree->line_style == GTK_CTREE_LINES_TABBED)
{
-
+
column_right = (COLUMN_LEFT_XPIXEL (clist, ctree->tree_column) +
clist->column[ctree->tree_column].area.width +
COLUMN_INSET);
column_left = (COLUMN_LEFT_XPIXEL (clist, ctree->tree_column) -
COLUMN_INSET - (ctree->tree_column != 0) * CELL_SPACING);
-
+
switch (clist->column[ctree->tree_column].justification)
{
case GTK_JUSTIFY_CENTER:
@@ -1680,7 +1680,7 @@ draw_row (GtkCList *clist,
case GTK_JUSTIFY_LEFT:
offset = (column_left + ctree->tree_indent *
(((GtkCTreeRow *)clist_row)->level - 1));
-
+
gdk_draw_line (clist->clist_window, ctree->lines_gc,
MIN (offset + TAB_SIZE, column_right),
cell_rectangle.y,
@@ -1689,7 +1689,7 @@ draw_row (GtkCList *clist,
case GTK_JUSTIFY_RIGHT:
offset = (column_right - 1 - ctree->tree_indent *
(((GtkCTreeRow *)clist_row)->level - 1));
-
+
gdk_draw_line (clist->clist_window, ctree->lines_gc,
-1, cell_rectangle.y,
MAX (offset - TAB_SIZE, column_left),
@@ -1697,18 +1697,18 @@ draw_row (GtkCList *clist,
break;
}
}
-
+
/* the last row has to clear its bottom cell spacing too */
if (clist_row == clist->row_list_end->data)
{
cell_rectangle.y += clist->row_height + CELL_SPACING;
-
+
if (!area || gdk_rectangle_intersect (area, &cell_rectangle, crect))
{
gdk_draw_rectangle (clist->clist_window,
widget->style->base_gc[GTK_STATE_ACTIVE], TRUE,
crect->x, crect->y, crect->width, crect->height);
-
+
/* horizontal black lines */
if (ctree->line_style == GTK_CTREE_LINES_TABBED)
{
@@ -1738,43 +1738,44 @@ draw_row (GtkCList *clist,
}
}
}
-
+
for (last_column = clist->columns - 1;
last_column >= 0 && !clist->column[last_column].visible; last_column--)
;
-
+
/* iterate and draw all the columns (row cells) and draw their contents */
for (i = 0; i < clist->columns; i++)
{
GtkStyle *style;
GdkGC *fg_gc;
GdkGC *bg_gc;
-
+ PangoLayout *layout = NULL;
+ PangoRectangle logical_rect;
+
gint width;
gint height;
gint pixmap_width;
gint string_width;
gint old_offset;
- gint row_center_offset;
-
+
if (!clist->column[i].visible)
continue;
-
+
get_cell_style (clist, clist_row, state, i, &style, &fg_gc, &bg_gc);
-
+
/* calculate clipping region */
clip_rectangle.x = clist->column[i].area.x + clist->hoffset;
clip_rectangle.width = clist->column[i].area.width;
-
+
cell_rectangle.x = clip_rectangle.x - COLUMN_INSET - CELL_SPACING;
cell_rectangle.width = (clip_rectangle.width + 2 * COLUMN_INSET +
(1 + (i == last_column)) * CELL_SPACING);
cell_rectangle.y = clip_rectangle.y;
cell_rectangle.height = clip_rectangle.height;
-
+
string_width = 0;
pixmap_width = 0;
-
+
if (area && !gdk_rectangle_intersect (area, &cell_rectangle,
&intersect_rectangle))
{
@@ -1785,48 +1786,46 @@ draw_row (GtkCList *clist,
{
gdk_draw_rectangle (clist->clist_window, bg_gc, TRUE,
crect->x, crect->y, crect->width, crect->height);
-
- /* calculate real width for column justification */
+
+
+ layout = _gtk_clist_create_cell_layout (clist, clist_row, i);
+ if (layout)
+ {
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+ width = logical_rect.width / PANGO_SCALE;
+ }
+ else
+ width = 0;
+
switch (clist_row->cell[i].type)
{
- case GTK_CELL_TEXT:
- width = gdk_string_width
- (style->font, GTK_CELL_TEXT (clist_row->cell[i])->text);
- break;
case GTK_CELL_PIXMAP:
gdk_window_get_size
(GTK_CELL_PIXMAP (clist_row->cell[i])->pixmap, &pixmap_width,
&height);
- width = pixmap_width;
+ width += pixmap_width;
break;
case GTK_CELL_PIXTEXT:
if (GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap)
- gdk_window_get_size
- (GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
- &pixmap_width, &height);
-
- width = pixmap_width;
-
- if (GTK_CELL_PIXTEXT (clist_row->cell[i])->text)
{
- string_width = gdk_string_width
- (style->font, GTK_CELL_PIXTEXT (clist_row->cell[i])->text);
- width += string_width;
+ gdk_window_get_size
+ (GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
+ &pixmap_width, &height);
+ width += pixmap_width;
}
-
+
if (GTK_CELL_PIXTEXT (clist_row->cell[i])->text &&
GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap)
width += GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
-
+
if (i == ctree->tree_column)
width += (ctree->tree_indent *
((GtkCTreeRow *)clist_row)->level);
break;
default:
- continue;
break;
}
-
+
switch (clist->column[i].justification)
{
case GTK_JUSTIFY_LEFT:
@@ -1842,7 +1841,7 @@ draw_row (GtkCList *clist,
(clip_rectangle.width / 2) - (width / 2));
break;
};
-
+
if (i != ctree->tree_column)
{
offset += clist_row->cell[i].horizontal;
@@ -1868,25 +1867,21 @@ draw_row (GtkCList *clist,
(clip_rectangle.height - height) / 2,
pixmap_width, height);
offset += GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
+
+ /* Fall through */
case GTK_CELL_TEXT:
- if (style != GTK_WIDGET (clist)->style)
- row_center_offset = (((clist->row_height -
- style->font->ascent -
- style->font->descent - 1) / 2) +
- 1.5 + style->font->ascent);
- else
- row_center_offset = clist->row_center_offset;
-
- gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
- gdk_draw_string
- (clist->clist_window, style->font, fg_gc,
- offset,
- row_rectangle.y + row_center_offset +
- clist_row->cell[i].vertical,
- (clist_row->cell[i].type == GTK_CELL_PIXTEXT) ?
- GTK_CELL_PIXTEXT (clist_row->cell[i])->text :
- GTK_CELL_TEXT (clist_row->cell[i])->text);
- gdk_gc_set_clip_rectangle (fg_gc, NULL);
+ if (layout)
+ {
+ gint row_center_offset = 1.5 + (clist->row_height - logical_rect.height / PANGO_SCALE - 1) / 2;
+
+ gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
+ gdk_draw_layout (clist->clist_window, fg_gc,
+ offset,
+ row_rectangle.y + row_center_offset + clist_row->cell[i].vertical,
+ layout);
+ gdk_gc_set_clip_rectangle (fg_gc, NULL);
+ pango_layout_unref (layout);
+ }
break;
default:
break;
@@ -1894,37 +1889,41 @@ draw_row (GtkCList *clist,
continue;
}
}
-
+
if (bg_gc == clist->bg_gc)
gdk_gc_set_background (ctree->lines_gc, &clist_row->background);
-
+
/* draw ctree->tree_column */
cell_rectangle.y -= CELL_SPACING;
cell_rectangle.height += CELL_SPACING;
-
+
if (area && !gdk_rectangle_intersect (area, &cell_rectangle,
&intersect_rectangle))
- continue;
-
+ {
+ if (layout)
+ pango_layout_unref (layout);
+ continue;
+ }
+
/* draw lines */
offset = gtk_ctree_draw_lines (ctree, (GtkCTreeRow *)clist_row, row, i,
state, &clip_rectangle, &cell_rectangle,
crect, area, style);
-
+
/* draw expander */
offset = gtk_ctree_draw_expander (ctree, (GtkCTreeRow *)clist_row,
style, &clip_rectangle, offset);
-
+
if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
offset -= ctree->tree_spacing;
else
offset += ctree->tree_spacing;
-
+
if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
offset -= (pixmap_width + clist_row->cell[i].horizontal);
else
offset += clist_row->cell[i].horizontal;
-
+
old_offset = offset;
offset = draw_cell_pixmap (clist->clist_window, &clip_rectangle, fg_gc,
GTK_CELL_PIXTEXT (clist_row->cell[i])->pixmap,
@@ -1933,9 +1932,11 @@ draw_row (GtkCList *clist,
clip_rectangle.y + clist_row->cell[i].vertical
+ (clip_rectangle.height - height) / 2,
pixmap_width, height);
-
- if (string_width)
- {
+
+ if (layout)
+ {
+ gint row_center_offset = 1.5 + (clist->row_height - logical_rect.height / PANGO_SCALE - 1) / 2;
+
if (clist->column[i].justification == GTK_JUSTIFY_RIGHT)
{
offset = (old_offset - string_width);
@@ -1948,22 +1949,17 @@ draw_row (GtkCList *clist,
offset += GTK_CELL_PIXTEXT (clist_row->cell[i])->spacing;
}
- if (style != GTK_WIDGET (clist)->style)
- row_center_offset = (((clist->row_height - style->font->ascent -
- style->font->descent - 1) / 2) +
- 1.5 + style->font->ascent);
- else
- row_center_offset = clist->row_center_offset;
-
gdk_gc_set_clip_rectangle (fg_gc, &clip_rectangle);
- gdk_draw_string (clist->clist_window, style->font, fg_gc, offset,
- row_rectangle.y + row_center_offset +
- clist_row->cell[i].vertical,
- GTK_CELL_PIXTEXT (clist_row->cell[i])->text);
+ gdk_draw_layout (clist->clist_window, fg_gc,
+ offset,
+ row_rectangle.y + row_center_offset + clist_row->cell[i].vertical,
+ layout);
+
+ pango_layout_unref (layout);
}
gdk_gc_set_clip_rectangle (fg_gc, NULL);
}
-
+
/* draw focus rectangle */
if (clist->focus_row == row &&
GTK_WIDGET_CAN_FOCUS (widget) && GTK_WIDGET_HAS_FOCUS (widget))
@@ -1992,7 +1988,7 @@ tree_draw_node (GtkCTree *ctree,
GtkCList *clist;
clist = GTK_CLIST (ctree);
-
+
if (CLIST_UNFROZEN (clist) && gtk_ctree_is_viewable (ctree, node))
{
GtkCTreeNode *work;
@@ -2018,15 +2014,15 @@ gtk_ctree_last_visible (GtkCTree *ctree,
if (!node)
return NULL;
-
+
work = GTK_CTREE_ROW (node)->children;
-
+
if (!work || !GTK_CTREE_ROW (node)->expanded)
return node;
-
+
while (GTK_CTREE_ROW (work)->sibling)
work = GTK_CTREE_ROW (work)->sibling;
-
+
return gtk_ctree_last_visible (ctree, work);
}
@@ -2049,9 +2045,9 @@ gtk_ctree_link (GtkCTree *ctree,
g_return_if_fail (node != NULL);
g_return_if_fail (node != sibling);
g_return_if_fail (node != parent);
-
+
clist = GTK_CLIST (ctree);
-
+
if (update_focus_row && clist->selection_mode == GTK_SELECTION_EXTENDED)
{
GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
@@ -2061,26 +2057,26 @@ gtk_ctree_link (GtkCTree *ctree,
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
}
-
+
for (rows = 1, list_end = (GList *)node; list_end->next;
list_end = list_end->next)
rows++;
-
+
GTK_CTREE_ROW (node)->parent = parent;
GTK_CTREE_ROW (node)->sibling = sibling;
-
+
if (!parent || (parent && (gtk_ctree_is_viewable (ctree, parent) &&
GTK_CTREE_ROW (parent)->expanded)))
{
visible = TRUE;
clist->rows += rows;
}
-
+
if (parent)
work = (GList *)(GTK_CTREE_ROW (parent)->children);
else
work = clist->row_list;
-
+
if (sibling)
{
if (work != (GList *)sibling)
@@ -2089,7 +2085,7 @@ gtk_ctree_link (GtkCTree *ctree,
work = (GList *)(GTK_CTREE_ROW (work)->sibling);
GTK_CTREE_ROW (work)->sibling = node;
}
-
+
if (sibling == GTK_CTREE_NODE (clist->row_list))
clist->row_list = (GList *) node;
if (GTK_CTREE_NODE_PREV (sibling) &&
@@ -2157,19 +2153,19 @@ gtk_ctree_link (GtkCTree *ctree,
}
}
}
-
+
gtk_ctree_pre_recursive (ctree, node, tree_update_level, NULL);
-
+
if (clist->row_list_end == NULL ||
clist->row_list_end->next == (GList *)node)
clist->row_list_end = list_end;
-
+
if (visible && update_focus_row)
{
gint pos;
-
+
pos = g_list_position (clist->row_list, (GList *)node);
-
+
if (pos <= clist->focus_row)
{
clist->focus_row += rows;
@@ -2190,11 +2186,11 @@ gtk_ctree_unlink (GtkCTree *ctree,
GtkCTreeNode *work;
GtkCTreeNode *parent;
GList *list;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (node != NULL);
-
+
clist = GTK_CLIST (ctree);
if (update_focus_row && clist->selection_mode == GTK_SELECTION_EXTENDED)
@@ -2206,9 +2202,9 @@ gtk_ctree_unlink (GtkCTree *ctree,
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
}
-
+
visible = gtk_ctree_is_viewable (ctree, node);
-
+
/* clist->row_list_end unlinked ? */
if (visible &&
(GTK_CTREE_NODE_NEXT (node) == NULL ||
@@ -2216,7 +2212,7 @@ gtk_ctree_unlink (GtkCTree *ctree,
gtk_ctree_is_ancestor (ctree, node,
GTK_CTREE_NODE (clist->row_list_end)))))
clist->row_list_end = (GList *) (GTK_CTREE_NODE_PREV (node));
-
+
/* update list */
rows = 0;
level = GTK_CTREE_ROW (node)->level;
@@ -2226,11 +2222,11 @@ gtk_ctree_unlink (GtkCTree *ctree,
work = GTK_CTREE_NODE_NEXT (work);
rows++;
}
-
+
if (visible)
{
clist->rows -= (rows + 1);
-
+
if (update_focus_row)
{
gint pos;
@@ -2250,7 +2246,7 @@ gtk_ctree_unlink (GtkCTree *ctree,
clist->undo_anchor = clist->focus_row;
}
}
-
+
if (work)
{
list = (GList *)GTK_CTREE_NODE_PREV (work);
@@ -2258,14 +2254,14 @@ gtk_ctree_unlink (GtkCTree *ctree,
list = (GList *)work;
list->prev = (GList *)GTK_CTREE_NODE_PREV (node);
}
-
+
if (GTK_CTREE_NODE_PREV (node) &&
GTK_CTREE_NODE_NEXT (GTK_CTREE_NODE_PREV (node)) == node)
{
list = (GList *)GTK_CTREE_NODE_PREV (node);
list->next = (GList *)work;
}
-
+
/* update tree */
parent = GTK_CTREE_ROW (node)->parent;
if (parent)
@@ -2279,7 +2275,7 @@ gtk_ctree_unlink (GtkCTree *ctree,
else
{
GtkCTreeNode *sibling;
-
+
sibling = GTK_CTREE_ROW (parent)->children;
while (GTK_CTREE_ROW (sibling)->sibling != node)
sibling = GTK_CTREE_ROW (sibling)->sibling;
@@ -2293,7 +2289,7 @@ gtk_ctree_unlink (GtkCTree *ctree,
else
{
GtkCTreeNode *sibling;
-
+
sibling = GTK_CTREE_NODE (clist->row_list);
while (GTK_CTREE_ROW (sibling)->sibling != node)
sibling = GTK_CTREE_ROW (sibling)->sibling;
@@ -2309,42 +2305,42 @@ real_row_move (GtkCList *clist,
{
GtkCTree *ctree;
GtkCTreeNode *node;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CTREE (clist));
-
+
if (GTK_CLIST_AUTO_SORT (clist))
return;
-
+
if (source_row < 0 || source_row >= clist->rows ||
dest_row < 0 || dest_row >= clist->rows ||
source_row == dest_row)
return;
-
+
ctree = GTK_CTREE (clist);
node = GTK_CTREE_NODE (g_list_nth (clist->row_list, source_row));
-
+
if (source_row < dest_row)
{
GtkCTreeNode *work;
-
+
dest_row++;
work = GTK_CTREE_ROW (node)->children;
-
+
while (work && GTK_CTREE_ROW (work)->level > GTK_CTREE_ROW (node)->level)
{
work = GTK_CTREE_NODE_NEXT (work);
dest_row++;
}
-
+
if (dest_row > clist->rows)
dest_row = clist->rows;
}
-
+
if (dest_row < clist->rows)
{
GtkCTreeNode *sibling;
-
+
sibling = GTK_CTREE_NODE (g_list_nth (clist->row_list, dest_row));
gtk_ctree_move (ctree, node, GTK_CTREE_ROW (sibling)->parent, sibling);
}
@@ -2361,24 +2357,24 @@ real_tree_move (GtkCTree *ctree,
GtkCList *clist;
GtkCTreeNode *work;
gboolean visible = FALSE;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (node != NULL);
g_return_if_fail (!new_sibling ||
GTK_CTREE_ROW (new_sibling)->parent == new_parent);
-
+
if (new_parent && GTK_CTREE_ROW (new_parent)->is_leaf)
return;
-
+
/* new_parent != child of child */
for (work = new_parent; work; work = GTK_CTREE_ROW (work)->parent)
if (work == node)
return;
-
+
clist = GTK_CLIST (ctree);
-
+
visible = gtk_ctree_is_viewable (ctree, node);
-
+
if (clist->selection_mode == GTK_SELECTION_EXTENDED)
{
GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
@@ -2388,7 +2384,7 @@ real_tree_move (GtkCTree *ctree,
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
}
-
+
if (GTK_CLIST_AUTO_SORT (clist))
{
if (new_parent == GTK_CTREE_ROW (node)->parent)
@@ -2398,23 +2394,23 @@ real_tree_move (GtkCTree *ctree,
new_sibling = GTK_CTREE_ROW (new_parent)->children;
else
new_sibling = GTK_CTREE_NODE (clist->row_list);
-
+
while (new_sibling && clist->compare
(clist, GTK_CTREE_ROW (node), GTK_CTREE_ROW (new_sibling)) > 0)
new_sibling = GTK_CTREE_ROW (new_sibling)->sibling;
}
-
+
if (new_parent == GTK_CTREE_ROW (node)->parent &&
new_sibling == GTK_CTREE_ROW (node)->sibling)
return;
-
+
gtk_clist_freeze (clist);
-
+
work = NULL;
if (gtk_ctree_is_viewable (ctree, node) ||
gtk_ctree_is_viewable (ctree, new_sibling))
work = GTK_CTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
-
+
gtk_ctree_unlink (ctree, node, FALSE);
gtk_ctree_link (ctree, node, new_parent, new_sibling, FALSE);
@@ -2425,14 +2421,14 @@ real_tree_move (GtkCTree *ctree,
clist->focus_row = g_list_position (clist->row_list, (GList *)work);
clist->undo_anchor = clist->focus_row;
}
-
+
if (clist->column[ctree->tree_column].auto_resize &&
!GTK_CLIST_AUTO_RESIZE_BLOCKED (clist) &&
(visible || gtk_ctree_is_viewable (ctree, node)))
gtk_clist_set_column_width
(clist, ctree->tree_column,
gtk_clist_optimal_column_width (clist, ctree->tree_column));
-
+
gtk_clist_thaw (clist);
}
@@ -2442,12 +2438,12 @@ change_focus_row_expansion (GtkCTree *ctree,
{
GtkCList *clist;
GtkCTreeNode *node;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
clist = GTK_CLIST (ctree);
-
+
if (gdk_pointer_is_grabbed () && GTK_WIDGET_HAS_GRAB (ctree))
return;
@@ -2455,7 +2451,7 @@ change_focus_row_expansion (GtkCTree *ctree,
GTK_CTREE_NODE (g_list_nth (clist->row_list, clist->focus_row))) ||
GTK_CTREE_ROW (node)->is_leaf || !(GTK_CTREE_ROW (node)->children))
return;
-
+
switch (action)
{
case GTK_CTREE_EXPANSION_EXPAND:
@@ -2488,27 +2484,27 @@ real_tree_expand (GtkCTree *ctree,
GtkRequisition requisition;
gboolean visible;
gint level;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
if (!node || GTK_CTREE_ROW (node)->expanded || GTK_CTREE_ROW (node)->is_leaf)
return;
-
+
clist = GTK_CLIST (ctree);
GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
-
+
GTK_CTREE_ROW (node)->expanded = TRUE;
level = GTK_CTREE_ROW (node)->level;
-
+
visible = gtk_ctree_is_viewable (ctree, node);
/* get cell width if tree_column is auto resized */
if (visible && clist->column[ctree->tree_column].auto_resize &&
!GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
GTK_CLIST_GET_CLASS (clist)->cell_size_request
(clist, &GTK_CTREE_ROW (node)->row, ctree->tree_column, &requisition);
-
+
/* unref/unset closed pixmap */
if (GTK_CELL_PIXTEXT
(GTK_CTREE_ROW (node)->row.cell[ctree->tree_column])->pixmap)
@@ -2530,21 +2526,21 @@ real_tree_expand (GtkCTree *ctree,
(GTK_CTREE_ROW (node)->row.cell[ctree->tree_column])->mask = NULL;
}
}
-
+
/* set/ref opened pixmap */
if (GTK_CTREE_ROW (node)->pixmap_opened)
{
GTK_CELL_PIXTEXT
(GTK_CTREE_ROW (node)->row.cell[ctree->tree_column])->pixmap =
gdk_pixmap_ref (GTK_CTREE_ROW (node)->pixmap_opened);
-
+
if (GTK_CTREE_ROW (node)->mask_opened)
GTK_CELL_PIXTEXT
(GTK_CTREE_ROW (node)->row.cell[ctree->tree_column])->mask =
gdk_pixmap_ref (GTK_CTREE_ROW (node)->mask_opened);
}
-
-
+
+
work = GTK_CTREE_ROW (node)->children;
if (work)
{
@@ -2558,8 +2554,8 @@ real_tree_expand (GtkCTree *ctree,
{
cell_width = g_new0 (gint, clist->columns);
if (clist->column[ctree->tree_column].auto_resize)
- cell_width[ctree->tree_column] = requisition.width;
-
+ cell_width[ctree->tree_column] = requisition.width;
+
while (work)
{
/* search maximum cell widths of auto_resize columns */
@@ -2570,7 +2566,7 @@ real_tree_expand (GtkCTree *ctree,
(clist, &GTK_CTREE_ROW (work)->row, i, &requisition);
cell_width[i] = MAX (requisition.width, cell_width[i]);
}
-
+
list = (GList *)work;
work = GTK_CTREE_NODE_NEXT (work);
tmp++;
@@ -2583,22 +2579,22 @@ real_tree_expand (GtkCTree *ctree,
work = GTK_CTREE_NODE_NEXT (work);
tmp++;
}
-
+
list->next = (GList *)GTK_CTREE_NODE_NEXT (node);
-
+
if (GTK_CTREE_NODE_NEXT (node))
{
GList *tmp_list;
-
+
tmp_list = (GList *)GTK_CTREE_NODE_NEXT (node);
tmp_list->prev = list;
}
else
clist->row_list_end = list;
-
+
list = (GList *)node;
list->next = (GList *)(GTK_CTREE_ROW (node)->children);
-
+
if (visible)
{
/* resize auto_resize columns if needed */
@@ -2607,12 +2603,12 @@ real_tree_expand (GtkCTree *ctree,
cell_width[i] > clist->column[i].width)
gtk_clist_set_column_width (clist, i, cell_width[i]);
g_free (cell_width);
-
+
/* update focus_row position */
row = g_list_position (clist->row_list, (GList *)node);
if (row < clist->focus_row)
clist->focus_row += tmp;
-
+
clist->rows += tmp;
CLIST_REFRESH (clist);
}
@@ -2632,28 +2628,28 @@ real_tree_collapse (GtkCTree *ctree,
GtkRequisition requisition;
gboolean visible;
gint level;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
if (!node || !GTK_CTREE_ROW (node)->expanded ||
GTK_CTREE_ROW (node)->is_leaf)
return;
-
+
clist = GTK_CLIST (ctree);
-
+
GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
GTK_CTREE_ROW (node)->expanded = FALSE;
level = GTK_CTREE_ROW (node)->level;
-
+
visible = gtk_ctree_is_viewable (ctree, node);
/* get cell width if tree_column is auto resized */
if (visible && clist->column[ctree->tree_column].auto_resize &&
!GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
GTK_CLIST_GET_CLASS (clist)->cell_size_request
(clist, &GTK_CTREE_ROW (node)->row, ctree->tree_column, &requisition);
-
+
/* unref/unset opened pixmap */
if (GTK_CELL_PIXTEXT
(GTK_CTREE_ROW (node)->row.cell[ctree->tree_column])->pixmap)
@@ -2675,33 +2671,33 @@ real_tree_collapse (GtkCTree *ctree,
(GTK_CTREE_ROW (node)->row.cell[ctree->tree_column])->mask = NULL;
}
}
-
+
/* set/ref closed pixmap */
if (GTK_CTREE_ROW (node)->pixmap_closed)
{
GTK_CELL_PIXTEXT
(GTK_CTREE_ROW (node)->row.cell[ctree->tree_column])->pixmap =
gdk_pixmap_ref (GTK_CTREE_ROW (node)->pixmap_closed);
-
+
if (GTK_CTREE_ROW (node)->mask_closed)
GTK_CELL_PIXTEXT
(GTK_CTREE_ROW (node)->row.cell[ctree->tree_column])->mask =
gdk_pixmap_ref (GTK_CTREE_ROW (node)->mask_closed);
}
-
+
work = GTK_CTREE_ROW (node)->children;
if (work)
{
gint tmp = 0;
gint row;
GList *list;
-
+
while (work && GTK_CTREE_ROW (work)->level > level)
{
work = GTK_CTREE_NODE_NEXT (work);
tmp++;
}
-
+
if (work)
{
list = (GList *)node;
@@ -2717,12 +2713,12 @@ real_tree_collapse (GtkCTree *ctree,
list->next = NULL;
clist->row_list_end = (GList *)node;
}
-
+
if (visible)
{
/* resize auto_resize columns if needed */
auto_resize_columns (clist);
-
+
row = g_list_position (clist->row_list, (GList *)node);
if (row < clist->focus_row)
clist->focus_row -= tmp;
@@ -2735,7 +2731,7 @@ real_tree_collapse (GtkCTree *ctree,
/* resize tree_column if needed */
column_auto_resize (clist, &GTK_CTREE_ROW (node)->row, ctree->tree_column,
requisition.width);
-
+
}
static void
@@ -2746,17 +2742,17 @@ column_auto_resize (GtkCList *clist,
{
/* resize column if needed for auto_resize */
GtkRequisition requisition;
-
+
if (!clist->column[column].auto_resize ||
GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
return;
-
+
if (clist_row)
GTK_CLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
- column, &requisition);
+ column, &requisition);
else
requisition.width = 0;
-
+
if (requisition.width > clist->column[column].width)
gtk_clist_set_column_width (clist, column, requisition.width);
else if (requisition.width < old_width &&
@@ -2764,7 +2760,7 @@ column_auto_resize (GtkCList *clist,
{
GList *list;
gint new_width;
-
+
/* run a "gtk_clist_optimal_column_width" but break, if
* the column doesn't shrink */
if (GTK_CLIST_SHOW_TITLES (clist) && clist->column[column].button)
@@ -2772,7 +2768,7 @@ column_auto_resize (GtkCList *clist,
(CELL_SPACING + (2 * COLUMN_INSET)));
else
new_width = 0;
-
+
for (list = clist->row_list; list; list = list->next)
{
GTK_CLIST_GET_CLASS (clist)->cell_size_request
@@ -2790,10 +2786,10 @@ static void
auto_resize_columns (GtkCList *clist)
{
gint i;
-
+
if (GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
return;
-
+
for (i = 0; i < clist->columns; i++)
column_auto_resize (clist, NULL, i, clist->column[i].width);
}
@@ -2805,27 +2801,35 @@ cell_size_request (GtkCList *clist,
GtkRequisition *requisition)
{
GtkCTree *ctree;
- GtkStyle *style;
gint width;
gint height;
-
+ PangoLayout *layout;
+ PangoRectangle logical_rect;
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CTREE (clist));
g_return_if_fail (requisition != NULL);
-
+
ctree = GTK_CTREE (clist);
-
- get_cell_style (clist, clist_row, GTK_STATE_NORMAL, column, &style,
- NULL, NULL);
-
+
+ layout = _gtk_clist_create_cell_layout (clist, clist_row, column);
+ if (layout)
+ {
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+
+ requisition->width = logical_rect.width / PANGO_SCALE;
+ requisition->height = logical_rect.height / PANGO_SCALE;
+
+ pango_layout_unref (layout);
+ }
+ else
+ {
+ requisition->width = 0;
+ requisition->height = 0;
+ }
+
switch (clist_row->cell[column].type)
{
- case GTK_CELL_TEXT:
- requisition->width =
- gdk_string_width (style->font,
- GTK_CELL_TEXT (clist_row->cell[column])->text);
- requisition->height = style->font->ascent + style->font->descent;
- break;
case GTK_CELL_PIXTEXT:
if (GTK_CELL_PIXTEXT (clist_row->cell[column])->pixmap)
{
@@ -2836,13 +2840,10 @@ cell_size_request (GtkCList *clist,
}
else
width = height = 0;
+
+ requisition->width += width;
+ requisition->height = MAX (requisition->height, height);
- requisition->width = width +
- gdk_string_width (style->font,
- GTK_CELL_TEXT (clist_row->cell[column])->text);
-
- requisition->height = MAX (style->font->ascent + style->font->descent,
- height);
if (column == ctree->tree_column)
{
requisition->width += (ctree->tree_spacing + ctree->tree_indent *
@@ -2866,15 +2867,15 @@ cell_size_request (GtkCList *clist,
case GTK_CELL_PIXMAP:
gdk_window_get_size (GTK_CELL_PIXMAP (clist_row->cell[column])->pixmap,
&width, &height);
- requisition->width = width;
- requisition->height = height;
+ requisition->width += width;
+ requisition->height = MAX (requisition->height, height);
break;
default:
requisition->width = 0;
requisition->height = 0;
break;
}
-
+
requisition->width += clist_row->cell[column].horizontal;
requisition->height += clist_row->cell[column].vertical;
}
@@ -2892,28 +2893,28 @@ set_cell_contents (GtkCList *clist,
gboolean visible = FALSE;
GtkCTree *ctree;
GtkRequisition requisition;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CTREE (clist));
g_return_if_fail (clist_row != NULL);
-
+
ctree = GTK_CTREE (clist);
-
+
if (clist->column[column].auto_resize &&
!GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
{
GtkCTreeNode *parent;
-
+
parent = ((GtkCTreeRow *)clist_row)->parent;
if (!parent || (parent && GTK_CTREE_ROW (parent)->expanded &&
gtk_ctree_is_viewable (ctree, parent)))
{
visible = TRUE;
GTK_CLIST_GET_CLASS (clist)->cell_size_request (clist, clist_row,
- column, &requisition);
+ column, &requisition);
}
}
-
+
switch (clist_row->cell[column].type)
{
case GTK_CELL_EMPTY:
@@ -2946,11 +2947,11 @@ set_cell_contents (GtkCList *clist,
default:
break;
}
-
+
clist_row->cell[column].type = GTK_CELL_EMPTY;
if (column == ctree->tree_column && type != GTK_CELL_EMPTY)
type = GTK_CELL_PIXTEXT;
-
+
switch (type)
{
case GTK_CELL_TEXT:
@@ -3031,12 +3032,12 @@ set_node_info (GtkCTree *ctree,
if (GTK_CTREE_ROW (node)->mask_closed)
gdk_bitmap_unref (GTK_CTREE_ROW (node)->mask_closed);
}
-
+
GTK_CTREE_ROW (node)->pixmap_opened = NULL;
GTK_CTREE_ROW (node)->mask_opened = NULL;
GTK_CTREE_ROW (node)->pixmap_closed = NULL;
GTK_CTREE_ROW (node)->mask_closed = NULL;
-
+
if (pixmap_closed)
{
GTK_CTREE_ROW (node)->pixmap_closed = gdk_pixmap_ref (pixmap_closed);
@@ -3049,10 +3050,10 @@ set_node_info (GtkCTree *ctree,
if (mask_opened)
GTK_CTREE_ROW (node)->mask_opened = gdk_bitmap_ref (mask_opened);
}
-
+
GTK_CTREE_ROW (node)->is_leaf = is_leaf;
GTK_CTREE_ROW (node)->expanded = (is_leaf) ? FALSE : expanded;
-
+
if (GTK_CTREE_ROW (node)->expanded)
gtk_ctree_node_set_pixtext (ctree, node, ctree->tree_column,
text, spacing, pixmap_opened, mask_opened);
@@ -3087,12 +3088,12 @@ tree_update_level (GtkCTree *ctree,
{
if (!node)
return;
-
+
if (GTK_CTREE_ROW (node)->parent)
- GTK_CTREE_ROW (node)->level =
- GTK_CTREE_ROW (GTK_CTREE_ROW (node)->parent)->level + 1;
+ GTK_CTREE_ROW (node)->level =
+ GTK_CTREE_ROW (GTK_CTREE_ROW (node)->parent)->level + 1;
else
- GTK_CTREE_ROW (node)->level = 1;
+ GTK_CTREE_ROW (node)->level = 1;
}
static void
@@ -3150,7 +3151,7 @@ tree_toggle_expansion (GtkCTree *ctree,
{
if (!node)
return;
-
+
if (GTK_CTREE_ROW (node)->expanded)
gtk_signal_emit (GTK_OBJECT (ctree), ctree_signals[TREE_COLLAPSE], node);
else
@@ -3163,11 +3164,11 @@ row_new (GtkCTree *ctree)
GtkCList *clist;
GtkCTreeRow *ctree_row;
int i;
-
+
clist = GTK_CLIST (ctree);
ctree_row = g_chunk_new (GtkCTreeRow, clist->row_mem_chunk);
ctree_row->row.cell = g_chunk_new (GtkCell, clist->cell_mem_chunk);
-
+
for (i = 0; i < clist->columns; i++)
{
ctree_row->row.cell[i].type = GTK_CELL_EMPTY;
@@ -3175,9 +3176,9 @@ row_new (GtkCTree *ctree)
ctree_row->row.cell[i].horizontal = 0;
ctree_row->row.cell[i].style = NULL;
}
-
+
GTK_CELL_PIXTEXT (ctree_row->row.cell[ctree->tree_column])->text = NULL;
-
+
ctree_row->row.fg_set = FALSE;
ctree_row->row.bg_set = FALSE;
ctree_row->row.style = NULL;
@@ -3185,7 +3186,7 @@ row_new (GtkCTree *ctree)
ctree_row->row.state = GTK_STATE_NORMAL;
ctree_row->row.data = NULL;
ctree_row->row.destroy = NULL;
-
+
ctree_row->level = 0;
ctree_row->expanded = FALSE;
ctree_row->parent = NULL;
@@ -3205,9 +3206,9 @@ row_delete (GtkCTree *ctree,
{
GtkCList *clist;
gint i;
-
+
clist = GTK_CLIST (ctree);
-
+
for (i = 0; i < clist->columns; i++)
{
GTK_CLIST_GET_CLASS (clist)->set_cell_contents
@@ -3219,39 +3220,39 @@ row_delete (GtkCTree *ctree,
gtk_style_unref (ctree_row->row.cell[i].style);
}
}
-
+
if (ctree_row->row.style)
{
if (GTK_WIDGET_REALIZED (ctree))
gtk_style_detach (ctree_row->row.style);
gtk_style_unref (ctree_row->row.style);
}
-
+
if (ctree_row->pixmap_closed)
{
gdk_pixmap_unref (ctree_row->pixmap_closed);
if (ctree_row->mask_closed)
gdk_bitmap_unref (ctree_row->mask_closed);
}
-
+
if (ctree_row->pixmap_opened)
{
gdk_pixmap_unref (ctree_row->pixmap_opened);
if (ctree_row->mask_opened)
gdk_bitmap_unref (ctree_row->mask_opened);
}
-
+
if (ctree_row->row.destroy)
{
GtkDestroyNotify dnotify = ctree_row->row.destroy;
gpointer ddata = ctree_row->row.data;
-
+
ctree_row->row.destroy = NULL;
ctree_row->row.data = NULL;
-
+
dnotify (ddata);
}
-
+
g_mem_chunk_free (clist->cell_mem_chunk, ctree_row->row.cell);
g_mem_chunk_free (clist->row_mem_chunk, ctree_row);
}
@@ -3263,7 +3264,7 @@ real_select_row (GtkCList *clist,
GdkEvent *event)
{
GList *node;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CTREE (clist));
@@ -3280,10 +3281,10 @@ real_unselect_row (GtkCList *clist,
GdkEvent *event)
{
GList *node;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CTREE (clist));
-
+
if ((node = g_list_nth (clist->row_list, row)))
gtk_signal_emit (GTK_OBJECT (clist), ctree_signals[TREE_UNSELECT_ROW],
node, column);
@@ -3298,24 +3299,24 @@ real_tree_select (GtkCTree *ctree,
GList *list;
GtkCTreeNode *sel_row;
gboolean node_selected;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
if (!node || GTK_CTREE_ROW (node)->row.state == GTK_STATE_SELECTED ||
!GTK_CTREE_ROW (node)->row.selectable)
return;
-
+
clist = GTK_CLIST (ctree);
-
+
switch (clist->selection_mode)
{
case GTK_SELECTION_SINGLE:
case GTK_SELECTION_BROWSE:
-
+
node_selected = FALSE;
list = clist->selection;
-
+
while (list)
{
sel_row = list->data;
@@ -3327,16 +3328,16 @@ real_tree_select (GtkCTree *ctree,
gtk_signal_emit (GTK_OBJECT (ctree),
ctree_signals[TREE_UNSELECT_ROW], sel_row, column);
}
-
+
if (node_selected)
return;
-
+
default:
break;
}
-
+
GTK_CTREE_ROW (node)->row.state = GTK_STATE_SELECTED;
-
+
if (!clist->selection)
{
clist->selection = g_list_append (clist->selection, node);
@@ -3344,7 +3345,7 @@ real_tree_select (GtkCTree *ctree,
}
else
clist->selection_end = g_list_append (clist->selection_end, node)->next;
-
+
tree_draw_node (ctree, node);
}
@@ -3354,22 +3355,22 @@ real_tree_unselect (GtkCTree *ctree,
gint column)
{
GtkCList *clist;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
if (!node || GTK_CTREE_ROW (node)->row.state != GTK_STATE_SELECTED)
return;
-
+
clist = GTK_CLIST (ctree);
-
+
if (clist->selection_end && clist->selection_end->data == node)
clist->selection_end = clist->selection_end->prev;
-
+
clist->selection = g_list_remove (clist->selection, node);
GTK_CTREE_ROW (node)->row.state = GTK_STATE_NORMAL;
-
+
tree_draw_node (ctree, node);
}
@@ -3381,7 +3382,7 @@ select_row_recursive (GtkCTree *ctree,
if (!node || GTK_CTREE_ROW (node)->row.state == GTK_STATE_SELECTED ||
!GTK_CTREE_ROW (node)->row.selectable)
return;
-
+
GTK_CLIST (ctree)->undo_unselection =
g_list_prepend (GTK_CLIST (ctree)->undo_unselection, node);
gtk_ctree_select (ctree, node);
@@ -3395,40 +3396,40 @@ real_select_all (GtkCList *clist)
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CTREE (clist));
-
+
ctree = GTK_CTREE (clist);
-
+
switch (clist->selection_mode)
{
case GTK_SELECTION_SINGLE:
case GTK_SELECTION_BROWSE:
return;
-
+
case GTK_SELECTION_EXTENDED:
-
+
gtk_clist_freeze (clist);
-
+
g_list_free (clist->undo_selection);
g_list_free (clist->undo_unselection);
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
-
+
clist->anchor_state = GTK_STATE_SELECTED;
clist->anchor = -1;
clist->drag_pos = -1;
clist->undo_anchor = clist->focus_row;
-
+
for (node = GTK_CTREE_NODE (clist->row_list); node;
node = GTK_CTREE_NODE_NEXT (node))
gtk_ctree_pre_recursive (ctree, node, select_row_recursive, NULL);
-
+
gtk_clist_thaw (clist);
break;
-
+
case GTK_SELECTION_MULTIPLE:
gtk_ctree_select_recursive (ctree, NULL);
break;
-
+
default:
/* do nothing */
break;
@@ -3441,12 +3442,12 @@ real_unselect_all (GtkCList *clist)
GtkCTree *ctree;
GtkCTreeNode *node;
GList *list;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CTREE (clist));
ctree = GTK_CTREE (clist);
-
+
switch (clist->selection_mode)
{
case GTK_SELECTION_BROWSE:
@@ -3458,24 +3459,24 @@ real_unselect_all (GtkCList *clist)
return;
}
break;
-
+
case GTK_SELECTION_EXTENDED:
g_list_free (clist->undo_selection);
g_list_free (clist->undo_unselection);
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
-
+
clist->anchor = -1;
clist->drag_pos = -1;
clist->undo_anchor = clist->focus_row;
break;
-
+
default:
break;
}
-
+
list = clist->selection;
-
+
while (list)
{
node = list->data;
@@ -3500,20 +3501,20 @@ ctree_is_hot_spot (GtkCTree *ctree,
g_return_val_if_fail (ctree != NULL, FALSE);
g_return_val_if_fail (GTK_IS_CTREE (ctree), FALSE);
g_return_val_if_fail (node != NULL, FALSE);
-
+
clist = GTK_CLIST (ctree);
-
+
if (!clist->column[ctree->tree_column].visible ||
ctree->expander_style == GTK_CTREE_EXPANDER_NONE)
return FALSE;
-
+
tree_row = GTK_CTREE_ROW (node);
-
+
cell = GTK_CELL_PIXTEXT(tree_row->row.cell[ctree->tree_column]);
-
+
yu = (ROW_TOP_YPIXEL (clist, row) + (clist->row_height - PM_SIZE) / 2 -
(clist->row_height - 1) % 2);
-
+
if (clist->column[ctree->tree_column].justification == GTK_JUSTIFY_RIGHT)
xl = (clist->column[ctree->tree_column].area.x +
clist->column[ctree->tree_column].area.width - 1 + clist->hoffset -
@@ -3523,7 +3524,7 @@ ctree_is_hot_spot (GtkCTree *ctree,
xl = (clist->column[ctree->tree_column].area.x + clist->hoffset +
(tree_row->level - 1) * ctree->tree_indent +
(ctree->line_style == GTK_CTREE_LINES_TABBED) * 3);
-
+
return (x >= xl && x <= xl + PM_SIZE && y >= yu && y <= yu + PM_SIZE);
}
@@ -3545,27 +3546,27 @@ gtk_ctree_construct (GtkCTree *ctree,
gchar *titles[])
{
GtkCList *clist;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (GTK_OBJECT_CONSTRUCTED (ctree) == FALSE);
-
+
clist = GTK_CLIST (ctree);
-
+
clist->row_mem_chunk = g_mem_chunk_new ("ctree row mem chunk",
sizeof (GtkCTreeRow),
sizeof (GtkCTreeRow)
* CLIST_OPTIMUM_SIZE,
G_ALLOC_AND_FREE);
-
+
clist->cell_mem_chunk = g_mem_chunk_new ("ctree cell mem chunk",
sizeof (GtkCell) * columns,
sizeof (GtkCell) * columns
* CLIST_OPTIMUM_SIZE,
G_ALLOC_AND_FREE);
-
+
ctree->tree_column = tree_column;
-
+
gtk_clist_construct (clist, columns, titles);
}
@@ -3575,13 +3576,13 @@ gtk_ctree_new_with_titles (gint columns,
gchar *titles[])
{
GtkWidget *widget;
-
+
g_return_val_if_fail (columns > 0, NULL);
g_return_val_if_fail (tree_column >= 0 && tree_column < columns, NULL);
-
+
widget = gtk_type_new (GTK_TYPE_CTREE);
gtk_ctree_construct (GTK_CTREE (widget), columns, tree_column, titles);
-
+
return widget;
}
@@ -3600,17 +3601,17 @@ real_insert_row (GtkCList *clist,
GtkCTreeNode *parent = NULL;
GtkCTreeNode *sibling;
GtkCTreeNode *node;
-
+
g_return_val_if_fail (clist != NULL, -1);
g_return_val_if_fail (GTK_IS_CTREE (clist), -1);
-
+
sibling = GTK_CTREE_NODE (g_list_nth (clist->row_list, row));
if (sibling)
parent = GTK_CTREE_ROW (sibling)->parent;
-
+
node = gtk_ctree_insert_node (GTK_CTREE (clist), parent, sibling, text, 5,
NULL, NULL, NULL, NULL, TRUE, FALSE);
-
+
if (GTK_CLIST_AUTO_SORT (clist) || !sibling)
return g_list_position (clist->row_list, (GList *) node);
@@ -3635,33 +3636,33 @@ gtk_ctree_insert_node (GtkCTree *ctree,
GtkCTreeNode *node;
GList *list;
gint i;
-
+
g_return_val_if_fail (ctree != NULL, NULL);
g_return_val_if_fail (GTK_IS_CTREE (ctree), NULL);
if (sibling)
g_return_val_if_fail (GTK_CTREE_ROW (sibling)->parent == parent, NULL);
-
+
if (parent && GTK_CTREE_ROW (parent)->is_leaf)
return NULL;
-
+
clist = GTK_CLIST (ctree);
-
+
/* create the row */
new_row = row_new (ctree);
list = g_list_alloc ();
list->data = new_row;
node = GTK_CTREE_NODE (list);
-
+
if (text)
for (i = 0; i < clist->columns; i++)
if (text[i] && i != ctree->tree_column)
GTK_CLIST_GET_CLASS (clist)->set_cell_contents
(clist, &(new_row->row), i, GTK_CELL_TEXT, text[i], 0, NULL, NULL);
-
+
set_node_info (ctree, node, text ?
text[ctree->tree_column] : NULL, spacing, pixmap_closed,
mask_closed, pixmap_opened, mask_opened, is_leaf, expanded);
-
+
/* sorted insertion */
if (GTK_CLIST_AUTO_SORT (clist))
{
@@ -3669,14 +3670,14 @@ gtk_ctree_insert_node (GtkCTree *ctree,
sibling = GTK_CTREE_ROW (parent)->children;
else
sibling = GTK_CTREE_NODE (clist->row_list);
-
+
while (sibling && clist->compare
(clist, GTK_CTREE_ROW (node), GTK_CTREE_ROW (sibling)) > 0)
sibling = GTK_CTREE_ROW (sibling)->sibling;
}
-
+
gtk_ctree_link (ctree, node, parent, sibling, TRUE);
-
+
if (text && !GTK_CLIST_AUTO_RESIZE_BLOCKED (clist) &&
gtk_ctree_is_viewable (ctree, node))
{
@@ -3684,17 +3685,17 @@ gtk_ctree_insert_node (GtkCTree *ctree,
if (clist->column[i].auto_resize)
column_auto_resize (clist, &(new_row->row), i, 0);
}
-
+
if (clist->rows == 1)
{
clist->focus_row = 0;
if (clist->selection_mode == GTK_SELECTION_BROWSE)
gtk_ctree_select (ctree, node);
}
-
-
+
+
CLIST_REFRESH (clist);
-
+
return node;
}
@@ -3713,7 +3714,7 @@ gtk_ctree_insert_gnode (GtkCTree *ctree,
GList *list;
GNode *work;
guint depth = 1;
-
+
g_return_val_if_fail (ctree != NULL, NULL);
g_return_val_if_fail (GTK_IS_CTREE (ctree), NULL);
g_return_val_if_fail (gnode != NULL, NULL);
@@ -3722,38 +3723,38 @@ gtk_ctree_insert_gnode (GtkCTree *ctree,
g_return_val_if_fail (GTK_CTREE_ROW (sibling)->parent == parent, NULL);
clist = GTK_CLIST (ctree);
-
+
if (parent)
depth = GTK_CTREE_ROW (parent)->level + 1;
-
+
list = g_list_alloc ();
list->data = row_new (ctree);
cnode = GTK_CTREE_NODE (list);
-
+
gtk_clist_freeze (clist);
-
+
set_node_info (ctree, cnode, "", 0, NULL, NULL, NULL, NULL, TRUE, FALSE);
-
+
if (!func (ctree, depth, gnode, cnode, data))
{
tree_delete_row (ctree, cnode, NULL);
return NULL;
}
-
+
if (GTK_CLIST_AUTO_SORT (clist))
{
if (parent)
sibling = GTK_CTREE_ROW (parent)->children;
else
sibling = GTK_CTREE_NODE (clist->row_list);
-
+
while (sibling && clist->compare
(clist, GTK_CTREE_ROW (cnode), GTK_CTREE_ROW (sibling)) > 0)
sibling = GTK_CTREE_ROW (sibling)->sibling;
}
-
+
gtk_ctree_link (ctree, cnode, parent, sibling, TRUE);
-
+
for (work = g_node_last_child (gnode); work; work = work->prev)
{
new_child = gtk_ctree_insert_gnode (ctree, cnode, child,
@@ -3763,7 +3764,7 @@ gtk_ctree_insert_gnode (GtkCTree *ctree,
}
gtk_clist_thaw (clist);
-
+
return cnode;
}
@@ -3778,7 +3779,7 @@ gtk_ctree_export_to_gnode (GtkCTree *ctree,
GtkCTreeNode *work;
GNode *gnode;
gint depth;
-
+
g_return_val_if_fail (ctree != NULL, NULL);
g_return_val_if_fail (GTK_IS_CTREE (ctree), NULL);
g_return_val_if_fail (node != NULL, NULL);
@@ -3788,7 +3789,7 @@ gtk_ctree_export_to_gnode (GtkCTree *ctree,
g_return_val_if_fail (parent != NULL, NULL);
g_return_val_if_fail (sibling->parent == parent, NULL);
}
-
+
gnode = g_node_new (NULL);
depth = g_node_depth (parent) + 1;
@@ -3797,36 +3798,36 @@ gtk_ctree_export_to_gnode (GtkCTree *ctree,
g_node_destroy (gnode);
return NULL;
}
-
+
if (parent)
g_node_insert_before (parent, sibling, gnode);
-
+
if (!GTK_CTREE_ROW (node)->is_leaf)
{
GNode *new_sibling = NULL;
-
+
for (work = GTK_CTREE_ROW (node)->children; work;
work = GTK_CTREE_ROW (work)->sibling)
new_sibling = gtk_ctree_export_to_gnode (ctree, gnode, new_sibling,
work, func, data);
-
+
g_node_reverse_children (gnode);
}
-
+
return gnode;
}
-
+
static void
real_remove_row (GtkCList *clist,
gint row)
{
GtkCTreeNode *node;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CTREE (clist));
-
+
node = GTK_CTREE_NODE (g_list_nth (clist->row_list, row));
-
+
if (node)
gtk_ctree_remove_node (GTK_CTREE (clist), node);
}
@@ -3836,18 +3837,18 @@ gtk_ctree_remove_node (GtkCTree *ctree,
GtkCTreeNode *node)
{
GtkCList *clist;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
clist = GTK_CLIST (ctree);
-
+
gtk_clist_freeze (clist);
-
+
if (node)
{
gboolean visible;
-
+
visible = gtk_ctree_is_viewable (ctree, node);
gtk_ctree_unlink (ctree, node, TRUE);
gtk_ctree_post_recursive (ctree, node, GTK_CTREE_FUNC (tree_delete),
@@ -3855,12 +3856,12 @@ gtk_ctree_remove_node (GtkCTree *ctree,
if (clist->selection_mode == GTK_SELECTION_BROWSE && !clist->selection &&
clist->focus_row >= 0)
gtk_clist_select_row (clist, clist->focus_row, -1);
-
+
auto_resize_columns (clist);
}
else
gtk_clist_clear (clist);
-
+
gtk_clist_thaw (clist);
}
@@ -3870,17 +3871,17 @@ real_clear (GtkCList *clist)
GtkCTree *ctree;
GtkCTreeNode *work;
GtkCTreeNode *ptr;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CTREE (clist));
-
+
ctree = GTK_CTREE (clist);
-
+
/* remove all rows */
work = GTK_CTREE_NODE (clist->row_list);
clist->row_list = NULL;
clist->row_list_end = NULL;
-
+
GTK_CLIST_SET_FLAG (clist, CLIST_AUTO_RESIZE_BLOCKED);
while (work)
{
@@ -3890,7 +3891,7 @@ real_clear (GtkCList *clist)
NULL);
}
GTK_CLIST_UNSET_FLAG (clist, CLIST_AUTO_RESIZE_BLOCKED);
-
+
parent_class->clear (clist);
}
@@ -3909,23 +3910,23 @@ gtk_ctree_post_recursive (GtkCTree *ctree,
{
GtkCTreeNode *work;
GtkCTreeNode *tmp;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (func != NULL);
-
+
if (node)
work = GTK_CTREE_ROW (node)->children;
else
work = GTK_CTREE_NODE (GTK_CLIST (ctree)->row_list);
-
+
while (work)
{
tmp = GTK_CTREE_ROW (work)->sibling;
gtk_ctree_post_recursive (ctree, work, func, data);
work = tmp;
}
-
+
if (node)
func (ctree, node, data);
}
@@ -3939,22 +3940,22 @@ gtk_ctree_post_recursive_to_depth (GtkCTree *ctree,
{
GtkCTreeNode *work;
GtkCTreeNode *tmp;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (func != NULL);
-
+
if (depth < 0)
{
gtk_ctree_post_recursive (ctree, node, func, data);
return;
}
-
+
if (node)
work = GTK_CTREE_ROW (node)->children;
else
work = GTK_CTREE_NODE (GTK_CLIST (ctree)->row_list);
-
+
if (work && GTK_CTREE_ROW (work)->level <= depth)
{
while (work)
@@ -3964,7 +3965,7 @@ gtk_ctree_post_recursive_to_depth (GtkCTree *ctree,
work = tmp;
}
}
-
+
if (node && GTK_CTREE_ROW (node)->level <= depth)
func (ctree, node, data);
}
@@ -3977,11 +3978,11 @@ gtk_ctree_pre_recursive (GtkCTree *ctree,
{
GtkCTreeNode *work;
GtkCTreeNode *tmp;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (func != NULL);
-
+
if (node)
{
work = GTK_CTREE_ROW (node)->children;
@@ -3989,7 +3990,7 @@ gtk_ctree_pre_recursive (GtkCTree *ctree,
}
else
work = GTK_CTREE_NODE (GTK_CLIST (ctree)->row_list);
-
+
while (work)
{
tmp = GTK_CTREE_ROW (work)->sibling;
@@ -4007,17 +4008,17 @@ gtk_ctree_pre_recursive_to_depth (GtkCTree *ctree,
{
GtkCTreeNode *work;
GtkCTreeNode *tmp;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (func != NULL);
-
+
if (depth < 0)
{
gtk_ctree_pre_recursive (ctree, node, func, data);
return;
}
-
+
if (node)
{
work = GTK_CTREE_ROW (node)->children;
@@ -4026,7 +4027,7 @@ gtk_ctree_pre_recursive_to_depth (GtkCTree *ctree,
}
else
work = GTK_CTREE_NODE (GTK_CLIST (ctree)->row_list);
-
+
if (work && GTK_CTREE_ROW (work)->level <= depth)
{
while (work)
@@ -4043,19 +4044,19 @@ gtk_ctree_is_viewable (GtkCTree *ctree,
GtkCTreeNode *node)
{
GtkCTreeRow *work;
-
+
g_return_val_if_fail (ctree != NULL, FALSE);
g_return_val_if_fail (GTK_IS_CTREE (ctree), FALSE);
g_return_val_if_fail (node != NULL, FALSE);
-
+
work = GTK_CTREE_ROW (node);
-
+
while (work->parent && GTK_CTREE_ROW (work->parent)->expanded)
work = GTK_CTREE_ROW (work->parent);
-
+
if (!work->parent)
return TRUE;
-
+
return FALSE;
}
@@ -4065,10 +4066,10 @@ gtk_ctree_last (GtkCTree *ctree,
{
g_return_val_if_fail (ctree != NULL, NULL);
g_return_val_if_fail (GTK_IS_CTREE (ctree), NULL);
-
+
if (!node)
return NULL;
-
+
while (GTK_CTREE_ROW (node)->sibling)
node = GTK_CTREE_ROW (node)->sibling;
@@ -4092,7 +4093,7 @@ gtk_ctree_find_node_ptr (GtkCTree *ctree,
node = GTK_CTREE_ROW(ctree_row->parent)->children;
else
node = GTK_CTREE_NODE (GTK_CLIST (ctree)->row_list);
-
+
while (GTK_CTREE_ROW (node) != ctree_row)
node = GTK_CTREE_ROW (node)->sibling;
@@ -4105,10 +4106,10 @@ gtk_ctree_node_nth (GtkCTree *ctree,
{
g_return_val_if_fail (ctree != NULL, NULL);
g_return_val_if_fail (GTK_IS_CTREE (ctree), NULL);
-
+
if ((row < 0) || (row >= GTK_CLIST(ctree)->rows))
return NULL;
-
+
return GTK_CTREE_NODE (g_list_nth (GTK_CLIST (ctree)->row_list, row));
}
@@ -4119,10 +4120,10 @@ gtk_ctree_find (GtkCTree *ctree,
{
if (!child)
return FALSE;
-
+
if (!node)
node = GTK_CTREE_NODE (GTK_CLIST (ctree)->row_list);
-
+
while (node)
{
if (node == child)
@@ -4144,10 +4145,10 @@ gtk_ctree_is_ancestor (GtkCTree *ctree,
{
g_return_val_if_fail (GTK_IS_CTREE (ctree), FALSE);
g_return_val_if_fail (node != NULL, FALSE);
-
+
if (GTK_CTREE_ROW (node)->children)
return gtk_ctree_find (ctree, GTK_CTREE_ROW (node)->children, child);
-
+
return FALSE;
}
@@ -4180,23 +4181,23 @@ gtk_ctree_find_all_by_row_data (GtkCTree *ctree,
gpointer data)
{
GList *list = NULL;
-
+
g_return_val_if_fail (ctree != NULL, NULL);
g_return_val_if_fail (GTK_IS_CTREE (ctree), NULL);
-
+
/* if node == NULL then look in the whole tree */
if (!node)
node = GTK_CTREE_NODE (GTK_CLIST (ctree)->row_list);
-
+
while (node)
{
if (GTK_CTREE_ROW (node)->row.data == data)
list = g_list_append (list, node);
-
+
if (GTK_CTREE_ROW (node)->children)
{
GList *sub_list;
-
+
sub_list = gtk_ctree_find_all_by_row_data (ctree,
GTK_CTREE_ROW
(node)->children,
@@ -4215,12 +4216,12 @@ gtk_ctree_find_by_row_data_custom (GtkCTree *ctree,
GCompareFunc func)
{
GtkCTreeNode *work;
-
+
g_return_val_if_fail (func != NULL, NULL);
-
+
if (!node)
node = GTK_CTREE_NODE (GTK_CLIST (ctree)->row_list);
-
+
while (node)
{
if (!func (GTK_CTREE_ROW (node)->row.data, data))
@@ -4241,24 +4242,24 @@ gtk_ctree_find_all_by_row_data_custom (GtkCTree *ctree,
GCompareFunc func)
{
GList *list = NULL;
-
+
g_return_val_if_fail (ctree != NULL, NULL);
g_return_val_if_fail (GTK_IS_CTREE (ctree), NULL);
g_return_val_if_fail (func != NULL, NULL);
-
+
/* if node == NULL then look in the whole tree */
if (!node)
node = GTK_CTREE_NODE (GTK_CLIST (ctree)->row_list);
-
+
while (node)
{
if (!func (GTK_CTREE_ROW (node)->row.data, data))
list = g_list_append (list, node);
-
+
if (GTK_CTREE_ROW (node)->children)
{
GList *sub_list;
-
+
sub_list = gtk_ctree_find_all_by_row_data_custom (ctree,
GTK_CTREE_ROW
(node)->children,
@@ -4282,11 +4283,11 @@ gtk_ctree_is_hot_spot (GtkCTree *ctree,
g_return_val_if_fail (ctree != NULL, FALSE);
g_return_val_if_fail (GTK_IS_CTREE (ctree), FALSE);
-
+
if (gtk_clist_get_selection_info (GTK_CLIST (ctree), x, y, &row, &column))
if ((node = GTK_CTREE_NODE(g_list_nth (GTK_CLIST (ctree)->row_list, row))))
return ctree_is_hot_spot (ctree, node, row, x, y);
-
+
return FALSE;
}
@@ -4320,7 +4321,7 @@ gtk_ctree_expand (GtkCTree *ctree,
if (GTK_CTREE_ROW (node)->is_leaf)
return;
-
+
gtk_signal_emit (GTK_OBJECT (ctree), ctree_signals[TREE_EXPAND], node);
}
@@ -4330,23 +4331,23 @@ gtk_ctree_expand_recursive (GtkCTree *ctree,
{
GtkCList *clist;
gboolean thaw = FALSE;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
clist = GTK_CLIST (ctree);
-
+
if (node && GTK_CTREE_ROW (node)->is_leaf)
return;
-
+
if (CLIST_UNFROZEN (clist) && (!node || gtk_ctree_is_viewable (ctree, node)))
{
gtk_clist_freeze (clist);
thaw = TRUE;
}
-
+
gtk_ctree_post_recursive (ctree, node, GTK_CTREE_FUNC (tree_expand), NULL);
-
+
if (thaw)
gtk_clist_thaw (clist);
}
@@ -4358,24 +4359,24 @@ gtk_ctree_expand_to_depth (GtkCTree *ctree,
{
GtkCList *clist;
gboolean thaw = FALSE;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
clist = GTK_CLIST (ctree);
-
+
if (node && GTK_CTREE_ROW (node)->is_leaf)
return;
-
+
if (CLIST_UNFROZEN (clist) && (!node || gtk_ctree_is_viewable (ctree, node)))
{
gtk_clist_freeze (clist);
thaw = TRUE;
}
-
+
gtk_ctree_post_recursive_to_depth (ctree, node, depth,
GTK_CTREE_FUNC (tree_expand), NULL);
-
+
if (thaw)
gtk_clist_thaw (clist);
}
@@ -4390,7 +4391,7 @@ gtk_ctree_collapse (GtkCTree *ctree,
if (GTK_CTREE_ROW (node)->is_leaf)
return;
-
+
gtk_signal_emit (GTK_OBJECT (ctree), ctree_signals[TREE_COLLAPSE], node);
}
@@ -4401,21 +4402,21 @@ gtk_ctree_collapse_recursive (GtkCTree *ctree,
GtkCList *clist;
gboolean thaw = FALSE;
gint i;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
if (node && GTK_CTREE_ROW (node)->is_leaf)
return;
-
+
clist = GTK_CLIST (ctree);
-
+
if (CLIST_UNFROZEN (clist) && (!node || gtk_ctree_is_viewable (ctree, node)))
{
gtk_clist_freeze (clist);
thaw = TRUE;
}
-
+
GTK_CLIST_SET_FLAG (clist, CLIST_AUTO_RESIZE_BLOCKED);
gtk_ctree_post_recursive (ctree, node, GTK_CTREE_FUNC (tree_collapse), NULL);
GTK_CLIST_UNSET_FLAG (clist, CLIST_AUTO_RESIZE_BLOCKED);
@@ -4423,7 +4424,7 @@ gtk_ctree_collapse_recursive (GtkCTree *ctree,
if (clist->column[i].auto_resize)
gtk_clist_set_column_width (clist, i,
gtk_clist_optimal_column_width (clist, i));
-
+
if (thaw)
gtk_clist_thaw (clist);
}
@@ -4436,21 +4437,21 @@ gtk_ctree_collapse_to_depth (GtkCTree *ctree,
GtkCList *clist;
gboolean thaw = FALSE;
gint i;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
if (node && GTK_CTREE_ROW (node)->is_leaf)
return;
-
+
clist = GTK_CLIST (ctree);
-
+
if (CLIST_UNFROZEN (clist) && (!node || gtk_ctree_is_viewable (ctree, node)))
{
gtk_clist_freeze (clist);
thaw = TRUE;
}
-
+
GTK_CLIST_SET_FLAG (clist, CLIST_AUTO_RESIZE_BLOCKED);
gtk_ctree_post_recursive_to_depth (ctree, node, depth,
GTK_CTREE_FUNC (tree_collapse_to_depth),
@@ -4460,7 +4461,7 @@ gtk_ctree_collapse_to_depth (GtkCTree *ctree,
if (clist->column[i].auto_resize)
gtk_clist_set_column_width (clist, i,
gtk_clist_optimal_column_width (clist, i));
-
+
if (thaw)
gtk_clist_thaw (clist);
}
@@ -4475,7 +4476,7 @@ gtk_ctree_toggle_expansion (GtkCTree *ctree,
if (GTK_CTREE_ROW (node)->is_leaf)
return;
-
+
tree_toggle_expansion (ctree, node, NULL);
}
@@ -4485,15 +4486,15 @@ gtk_ctree_toggle_expansion_recursive (GtkCTree *ctree,
{
GtkCList *clist;
gboolean thaw = FALSE;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
if (node && GTK_CTREE_ROW (node)->is_leaf)
return;
-
+
clist = GTK_CLIST (ctree);
-
+
if (CLIST_UNFROZEN (clist) && (!node || gtk_ctree_is_viewable (ctree, node)))
{
gtk_clist_freeze (clist);
@@ -4502,7 +4503,7 @@ gtk_ctree_toggle_expansion_recursive (GtkCTree *ctree,
gtk_ctree_post_recursive (ctree, node,
GTK_CTREE_FUNC (tree_toggle_expansion), NULL);
-
+
if (thaw)
gtk_clist_thaw (clist);
}
@@ -4514,7 +4515,7 @@ gtk_ctree_select (GtkCTree *ctree,
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (node != NULL);
-
+
if (GTK_CTREE_ROW (node)->row.selectable)
gtk_signal_emit (GTK_OBJECT (ctree), ctree_signals[TREE_SELECT_ROW],
node, -1);
@@ -4527,7 +4528,7 @@ gtk_ctree_unselect (GtkCTree *ctree,
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (node != NULL);
-
+
gtk_signal_emit (GTK_OBJECT (ctree), ctree_signals[TREE_UNSELECT_ROW],
node, -1);
}
@@ -4553,24 +4554,24 @@ gtk_ctree_real_select_recursive (GtkCTree *ctree,
{
GtkCList *clist;
gboolean thaw = FALSE;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
clist = GTK_CLIST (ctree);
-
+
if ((state &&
(clist->selection_mode == GTK_SELECTION_BROWSE ||
clist->selection_mode == GTK_SELECTION_SINGLE)) ||
(!state && clist->selection_mode == GTK_SELECTION_BROWSE))
return;
-
+
if (CLIST_UNFROZEN (clist) && (!node || gtk_ctree_is_viewable (ctree, node)))
{
gtk_clist_freeze (clist);
thaw = TRUE;
}
-
+
if (clist->selection_mode == GTK_SELECTION_EXTENDED)
{
GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
@@ -4580,7 +4581,7 @@ gtk_ctree_real_select_recursive (GtkCTree *ctree,
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
}
-
+
if (state)
gtk_ctree_post_recursive (ctree, node,
GTK_CTREE_FUNC (tree_select), NULL);
@@ -4605,20 +4606,20 @@ gtk_ctree_node_set_text (GtkCTree *ctree,
const gchar *text)
{
GtkCList *clist;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (node != NULL);
-
+
if (column < 0 || column >= GTK_CLIST (ctree)->columns)
return;
clist = GTK_CLIST (ctree);
-
+
GTK_CLIST_GET_CLASS (clist)->set_cell_contents
(clist, &(GTK_CTREE_ROW(node)->row), column, GTK_CELL_TEXT,
text, 0, NULL, NULL);
-
+
tree_draw_node (ctree, node);
}
@@ -4630,25 +4631,25 @@ gtk_ctree_node_set_pixmap (GtkCTree *ctree,
GdkBitmap *mask)
{
GtkCList *clist;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (node != NULL);
g_return_if_fail (pixmap != NULL);
-
+
if (column < 0 || column >= GTK_CLIST (ctree)->columns)
return;
-
+
gdk_pixmap_ref (pixmap);
if (mask)
gdk_pixmap_ref (mask);
-
+
clist = GTK_CLIST (ctree);
-
+
GTK_CLIST_GET_CLASS (clist)->set_cell_contents
(clist, &(GTK_CTREE_ROW (node)->row), column, GTK_CELL_PIXMAP,
NULL, 0, pixmap, mask);
-
+
tree_draw_node (ctree, node);
}
@@ -4662,7 +4663,7 @@ gtk_ctree_node_set_pixtext (GtkCTree *ctree,
GdkBitmap *mask)
{
GtkCList *clist;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (node != NULL);
@@ -4670,20 +4671,20 @@ gtk_ctree_node_set_pixtext (GtkCTree *ctree,
g_return_if_fail (pixmap != NULL);
if (column < 0 || column >= GTK_CLIST (ctree)->columns)
return;
-
+
clist = GTK_CLIST (ctree);
-
+
if (pixmap)
{
gdk_pixmap_ref (pixmap);
if (mask)
gdk_pixmap_ref (mask);
}
-
+
GTK_CLIST_GET_CLASS (clist)->set_cell_contents
(clist, &(GTK_CTREE_ROW (node)->row), column, GTK_CELL_PIXTEXT,
text, spacing, pixmap, mask);
-
+
tree_draw_node (ctree, node);
}
@@ -4701,14 +4702,14 @@ gtk_ctree_set_node_info (GtkCTree *ctree,
{
gboolean old_leaf;
gboolean old_expanded;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (node != NULL);
-
+
old_leaf = GTK_CTREE_ROW (node)->is_leaf;
old_expanded = GTK_CTREE_ROW (node)->expanded;
-
+
if (is_leaf && GTK_CTREE_ROW (node)->children)
{
GtkCTreeNode *work;
@@ -4722,10 +4723,10 @@ gtk_ctree_set_node_info (GtkCTree *ctree,
gtk_ctree_remove_node (ctree, ptr);
}
}
-
+
set_node_info (ctree, node, text, spacing, pixmap_closed, mask_closed,
pixmap_opened, mask_opened, is_leaf, expanded);
-
+
if (!is_leaf && !old_leaf)
{
GTK_CTREE_ROW (node)->expanded = old_expanded;
@@ -4734,7 +4735,7 @@ gtk_ctree_set_node_info (GtkCTree *ctree,
else if (!expanded && old_expanded)
gtk_ctree_collapse (ctree, node);
}
-
+
GTK_CTREE_ROW (node)->expanded = (is_leaf) ? FALSE : expanded;
tree_draw_node (ctree, node);
@@ -4750,16 +4751,16 @@ gtk_ctree_node_set_shift (GtkCTree *ctree,
GtkCList *clist;
GtkRequisition requisition;
gboolean visible = FALSE;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (node != NULL);
-
+
if (column < 0 || column >= GTK_CLIST (ctree)->columns)
return;
-
+
clist = GTK_CLIST (ctree);
-
+
if (clist->column[column].auto_resize &&
!GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
{
@@ -4768,14 +4769,14 @@ gtk_ctree_node_set_shift (GtkCTree *ctree,
GTK_CLIST_GET_CLASS (clist)->cell_size_request
(clist, &GTK_CTREE_ROW (node)->row, column, &requisition);
}
-
+
GTK_CTREE_ROW (node)->row.cell[column].vertical = vertical;
GTK_CTREE_ROW (node)->row.cell[column].horizontal = horizontal;
-
+
if (visible)
column_auto_resize (clist, &GTK_CTREE_ROW (node)->row,
column, requisition.width);
-
+
tree_draw_node (ctree, node);
}
@@ -4787,13 +4788,13 @@ remove_grab (GtkCList *clist)
gtk_grab_remove (GTK_WIDGET (clist));
gdk_pointer_ungrab (GDK_CURRENT_TIME);
}
-
+
if (clist->htimer)
{
gtk_timeout_remove (clist->htimer);
clist->htimer = 0;
}
-
+
if (clist->vtimer)
{
gtk_timeout_remove (clist->vtimer);
@@ -4809,24 +4810,24 @@ gtk_ctree_node_set_selectable (GtkCTree *ctree,
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (node != NULL);
-
+
if (selectable == GTK_CTREE_ROW (node)->row.selectable)
return;
-
+
GTK_CTREE_ROW (node)->row.selectable = selectable;
-
+
if (!selectable && GTK_CTREE_ROW (node)->row.state == GTK_STATE_SELECTED)
{
GtkCList *clist;
-
+
clist = GTK_CLIST (ctree);
-
+
if (clist->anchor >= 0 &&
clist->selection_mode == GTK_SELECTION_EXTENDED)
{
clist->drag_button = 0;
remove_grab (clist);
-
+
GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
}
gtk_ctree_unselect (ctree, node);
@@ -4838,7 +4839,7 @@ gtk_ctree_node_get_selectable (GtkCTree *ctree,
GtkCTreeNode *node)
{
g_return_val_if_fail (node != NULL, FALSE);
-
+
return GTK_CTREE_ROW (node)->row.selectable;
}
@@ -4850,10 +4851,10 @@ gtk_ctree_node_get_cell_type (GtkCTree *ctree,
g_return_val_if_fail (ctree != NULL, -1);
g_return_val_if_fail (GTK_IS_CTREE (ctree), -1);
g_return_val_if_fail (node != NULL, -1);
-
+
if (column < 0 || column >= GTK_CLIST (ctree)->columns)
return -1;
-
+
return GTK_CTREE_ROW (node)->row.cell[column].type;
}
@@ -4866,16 +4867,16 @@ gtk_ctree_node_get_text (GtkCTree *ctree,
g_return_val_if_fail (ctree != NULL, 0);
g_return_val_if_fail (GTK_IS_CTREE (ctree), 0);
g_return_val_if_fail (node != NULL, 0);
-
+
if (column < 0 || column >= GTK_CLIST (ctree)->columns)
return 0;
-
+
if (GTK_CTREE_ROW (node)->row.cell[column].type != GTK_CELL_TEXT)
return 0;
-
+
if (text)
*text = GTK_CELL_TEXT (GTK_CTREE_ROW (node)->row.cell[column])->text;
-
+
return 1;
}
@@ -4889,18 +4890,18 @@ gtk_ctree_node_get_pixmap (GtkCTree *ctree,
g_return_val_if_fail (ctree != NULL, 0);
g_return_val_if_fail (GTK_IS_CTREE (ctree), 0);
g_return_val_if_fail (node != NULL, 0);
-
+
if (column < 0 || column >= GTK_CLIST (ctree)->columns)
return 0;
-
+
if (GTK_CTREE_ROW (node)->row.cell[column].type != GTK_CELL_PIXMAP)
return 0;
-
+
if (pixmap)
*pixmap = GTK_CELL_PIXMAP (GTK_CTREE_ROW(node)->row.cell[column])->pixmap;
if (mask)
*mask = GTK_CELL_PIXMAP (GTK_CTREE_ROW (node)->row.cell[column])->mask;
-
+
return 1;
}
@@ -4984,19 +4985,19 @@ gtk_ctree_node_set_cell_style (GtkCTree *ctree,
GtkCList *clist;
GtkRequisition requisition;
gboolean visible = FALSE;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (node != NULL);
-
+
clist = GTK_CLIST (ctree);
-
+
if (column < 0 || column >= clist->columns)
return;
-
+
if (GTK_CTREE_ROW (node)->row.cell[column].style == style)
return;
-
+
if (clist->column[column].auto_resize &&
!GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
{
@@ -5005,16 +5006,16 @@ gtk_ctree_node_set_cell_style (GtkCTree *ctree,
GTK_CLIST_GET_CLASS (clist)->cell_size_request
(clist, &GTK_CTREE_ROW (node)->row, column, &requisition);
}
-
+
if (GTK_CTREE_ROW (node)->row.cell[column].style)
{
if (GTK_WIDGET_REALIZED (ctree))
gtk_style_detach (GTK_CTREE_ROW (node)->row.cell[column].style);
gtk_style_unref (GTK_CTREE_ROW (node)->row.cell[column].style);
}
-
+
GTK_CTREE_ROW (node)->row.cell[column].style = style;
-
+
if (GTK_CTREE_ROW (node)->row.cell[column].style)
{
gtk_style_ref (GTK_CTREE_ROW (node)->row.cell[column].style);
@@ -5024,11 +5025,11 @@ gtk_ctree_node_set_cell_style (GtkCTree *ctree,
gtk_style_attach (GTK_CTREE_ROW (node)->row.cell[column].style,
clist->clist_window);
}
-
+
if (visible)
column_auto_resize (clist, &GTK_CTREE_ROW (node)->row, column,
requisition.width);
-
+
tree_draw_node (ctree, node);
}
@@ -5040,10 +5041,10 @@ gtk_ctree_node_get_cell_style (GtkCTree *ctree,
g_return_val_if_fail (ctree != NULL, NULL);
g_return_val_if_fail (GTK_IS_CTREE (ctree), NULL);
g_return_val_if_fail (node != NULL, NULL);
-
+
if (column < 0 || column >= GTK_CLIST (ctree)->columns)
return NULL;
-
+
return GTK_CTREE_ROW (node)->row.cell[column].style;
}
@@ -5057,13 +5058,13 @@ gtk_ctree_node_set_row_style (GtkCTree *ctree,
gboolean visible;
gint *old_width = NULL;
gint i;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (node != NULL);
-
+
clist = GTK_CLIST (ctree);
-
+
if (GTK_CTREE_ROW (node)->row.style == style)
return;
@@ -5079,16 +5080,16 @@ gtk_ctree_node_set_row_style (GtkCTree *ctree,
old_width[i] = requisition.width;
}
}
-
+
if (GTK_CTREE_ROW (node)->row.style)
{
if (GTK_WIDGET_REALIZED (ctree))
gtk_style_detach (GTK_CTREE_ROW (node)->row.style);
gtk_style_unref (GTK_CTREE_ROW (node)->row.style);
}
-
+
GTK_CTREE_ROW (node)->row.style = style;
-
+
if (GTK_CTREE_ROW (node)->row.style)
{
gtk_style_ref (GTK_CTREE_ROW (node)->row.style);
@@ -5098,7 +5099,7 @@ gtk_ctree_node_set_row_style (GtkCTree *ctree,
gtk_style_attach (GTK_CTREE_ROW (node)->row.style,
clist->clist_window);
}
-
+
if (visible && !GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
{
for (i = 0; i < clist->columns; i++)
@@ -5117,7 +5118,7 @@ gtk_ctree_node_get_row_style (GtkCTree *ctree,
g_return_val_if_fail (ctree != NULL, NULL);
g_return_val_if_fail (GTK_IS_CTREE (ctree), NULL);
g_return_val_if_fail (node != NULL, NULL);
-
+
return GTK_CTREE_ROW (node)->row.style;
}
@@ -5129,7 +5130,7 @@ gtk_ctree_node_set_foreground (GtkCTree *ctree,
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (node != NULL);
-
+
if (color)
{
GTK_CTREE_ROW (node)->row.foreground = *color;
@@ -5140,7 +5141,7 @@ gtk_ctree_node_set_foreground (GtkCTree *ctree,
}
else
GTK_CTREE_ROW (node)->row.fg_set = FALSE;
-
+
tree_draw_node (ctree, node);
}
@@ -5152,7 +5153,7 @@ gtk_ctree_node_set_background (GtkCTree *ctree,
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (node != NULL);
-
+
if (color)
{
GTK_CTREE_ROW (node)->row.background = *color;
@@ -5163,7 +5164,7 @@ gtk_ctree_node_set_background (GtkCTree *ctree,
}
else
GTK_CTREE_ROW (node)->row.bg_set = FALSE;
-
+
tree_draw_node (ctree, node);
}
@@ -5187,13 +5188,13 @@ gtk_ctree_node_set_row_data_full (GtkCTree *ctree,
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (node != NULL);
-
+
dnotify = GTK_CTREE_ROW (node)->row.destroy;
ddata = GTK_CTREE_ROW (node)->row.data;
GTK_CTREE_ROW (node)->row.data = data;
GTK_CTREE_ROW (node)->row.destroy = destroy;
-
+
if (dnotify)
dnotify (ddata);
}
@@ -5204,7 +5205,7 @@ gtk_ctree_node_get_row_data (GtkCTree *ctree,
{
g_return_val_if_fail (ctree != NULL, NULL);
g_return_val_if_fail (GTK_IS_CTREE (ctree), NULL);
-
+
return node ? GTK_CTREE_ROW (node)->row.data : NULL;
}
@@ -5217,15 +5218,15 @@ gtk_ctree_node_moveto (GtkCTree *ctree,
{
gint row = -1;
GtkCList *clist;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
clist = GTK_CLIST (ctree);
-
+
while (node && !gtk_ctree_is_viewable (ctree, node))
node = GTK_CTREE_ROW (node)->parent;
-
+
if (node)
row = g_list_position (clist->row_list, (GList *)node);
@@ -5254,17 +5255,17 @@ gtk_ctree_set_indent (GtkCTree *ctree,
gint indent)
{
GtkCList *clist;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (indent >= 0);
-
+
if (indent == ctree->tree_indent)
return;
-
+
clist = GTK_CLIST (ctree);
ctree->tree_indent = indent;
-
+
if (clist->column[ctree->tree_column].auto_resize &&
!GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
gtk_clist_set_column_width
@@ -5280,19 +5281,19 @@ gtk_ctree_set_spacing (GtkCTree *ctree,
{
GtkCList *clist;
gint old_spacing;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
g_return_if_fail (spacing >= 0);
-
+
if (spacing == ctree->tree_spacing)
return;
-
+
clist = GTK_CLIST (ctree);
-
+
old_spacing = ctree->tree_spacing;
ctree->tree_spacing = spacing;
-
+
if (clist->column[ctree->tree_column].auto_resize &&
!GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
gtk_clist_set_column_width (clist, ctree->tree_column,
@@ -5308,16 +5309,16 @@ gtk_ctree_set_show_stub (GtkCTree *ctree,
{
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
show_stub = show_stub != FALSE;
-
+
if (show_stub != ctree->show_stub)
{
GtkCList *clist;
-
+
clist = GTK_CLIST (ctree);
ctree->show_stub = show_stub;
-
+
if (CLIST_UNFROZEN (clist) && clist->rows &&
gtk_clist_row_is_visible (clist, 0) != GTK_VISIBILITY_NONE)
GTK_CLIST_GET_CLASS (clist)->draw_row
@@ -5331,18 +5332,18 @@ gtk_ctree_set_line_style (GtkCTree *ctree,
{
GtkCList *clist;
GtkCTreeLineStyle old_style;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
if (line_style == ctree->line_style)
return;
-
+
clist = GTK_CLIST (ctree);
-
+
old_style = ctree->line_style;
ctree->line_style = line_style;
-
+
if (clist->column[ctree->tree_column].auto_resize &&
!GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
{
@@ -5355,7 +5356,7 @@ gtk_ctree_set_line_style (GtkCTree *ctree,
(clist, ctree->tree_column,
clist->column[ctree->tree_column].width + 3);
}
-
+
if (GTK_WIDGET_REALIZED (ctree))
{
switch (line_style)
@@ -5391,23 +5392,23 @@ gtk_ctree_set_expander_style (GtkCTree *ctree,
{
GtkCList *clist;
GtkCTreeExpanderStyle old_style;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
if (expander_style == ctree->expander_style)
return;
-
+
clist = GTK_CLIST (ctree);
-
+
old_style = ctree->expander_style;
ctree->expander_style = expander_style;
-
+
if (clist->column[ctree->tree_column].auto_resize &&
!GTK_CLIST_AUTO_RESIZE_BLOCKED (clist))
{
gint new_width;
-
+
new_width = clist->column[ctree->tree_column].width;
switch (old_style)
{
@@ -5421,7 +5422,7 @@ gtk_ctree_set_expander_style (GtkCTree *ctree,
new_width -= PM_SIZE + 1;
break;
}
-
+
switch (expander_style)
{
case GTK_CTREE_EXPANDER_NONE:
@@ -5434,10 +5435,10 @@ gtk_ctree_set_expander_style (GtkCTree *ctree,
new_width += PM_SIZE + 1;
break;
}
-
+
gtk_clist_set_column_width (clist, ctree->tree_column, new_width);
}
-
+
if (GTK_WIDGET_DRAWABLE (clist))
CLIST_REFRESH (clist);
}
@@ -5457,14 +5458,14 @@ tree_sort (GtkCTree *ctree,
GtkCTreeNode *cmp;
GtkCTreeNode *work;
GtkCList *clist;
-
+
clist = GTK_CLIST (ctree);
-
+
if (node)
list_start = GTK_CTREE_ROW (node)->children;
else
list_start = GTK_CTREE_NODE (clist->row_list);
-
+
while (list_start)
{
cmp = list_start;
@@ -5501,14 +5502,14 @@ gtk_ctree_sort_recursive (GtkCTree *ctree,
{
GtkCList *clist;
GtkCTreeNode *focus_node = NULL;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
clist = GTK_CLIST (ctree);
-
+
gtk_clist_freeze (clist);
-
+
if (clist->selection_mode == GTK_SELECTION_EXTENDED)
{
GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
@@ -5518,22 +5519,22 @@ gtk_ctree_sort_recursive (GtkCTree *ctree,
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
}
-
+
if (!node || (node && gtk_ctree_is_viewable (ctree, node)))
focus_node =
GTK_CTREE_NODE (g_list_nth (clist->row_list, clist->focus_row));
-
+
gtk_ctree_post_recursive (ctree, node, GTK_CTREE_FUNC (tree_sort), NULL);
-
+
if (!node)
tree_sort (ctree, NULL, NULL);
-
+
if (focus_node)
{
clist->focus_row = g_list_position (clist->row_list,(GList *)focus_node);
clist->undo_anchor = clist->focus_row;
}
-
+
gtk_clist_thaw (clist);
}
@@ -5549,14 +5550,14 @@ gtk_ctree_sort_node (GtkCTree *ctree,
{
GtkCList *clist;
GtkCTreeNode *focus_node = NULL;
-
+
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
clist = GTK_CLIST (ctree);
-
+
gtk_clist_freeze (clist);
-
+
if (clist->selection_mode == GTK_SELECTION_EXTENDED)
{
GTK_CLIST_GET_CLASS (clist)->resync_selection (clist, NULL);
@@ -5566,19 +5567,19 @@ gtk_ctree_sort_node (GtkCTree *ctree,
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
}
-
+
if (!node || (node && gtk_ctree_is_viewable (ctree, node)))
focus_node = GTK_CTREE_NODE
(g_list_nth (clist->row_list, clist->focus_row));
-
+
tree_sort (ctree, node, NULL);
-
+
if (focus_node)
{
clist->focus_row = g_list_position (clist->row_list,(GList *)focus_node);
clist->undo_anchor = clist->focus_row;
}
-
+
gtk_clist_thaw (clist);
}
@@ -5590,7 +5591,7 @@ fake_unselect_all (GtkCList *clist,
{
GList *list;
GList *focus_node = NULL;
-
+
if (row >= 0 && (focus_node = g_list_nth (clist->row_list, row)))
{
if (GTK_CTREE_ROW (focus_node)->row.state == GTK_STATE_NORMAL &&
@@ -5601,10 +5602,10 @@ fake_unselect_all (GtkCList *clist,
if (CLIST_UNFROZEN (clist) &&
gtk_clist_row_is_visible (clist, row) != GTK_VISIBILITY_NONE)
GTK_CLIST_GET_CLASS (clist)->draw_row (clist, NULL, row,
- GTK_CLIST_ROW (focus_node));
+ GTK_CLIST_ROW (focus_node));
}
}
-
+
clist->undo_selection = clist->selection;
clist->selection = NULL;
clist->selection_end = NULL;
@@ -5613,7 +5614,7 @@ fake_unselect_all (GtkCList *clist,
{
if (list->data == focus_node)
continue;
-
+
GTK_CTREE_ROW ((GList *)(list->data))->row.state = GTK_STATE_NORMAL;
tree_draw_node (GTK_CTREE (clist), GTK_CTREE_NODE (list->data));
}
@@ -5637,23 +5638,23 @@ resync_selection (GtkCList *clist, GdkEvent *event)
gint e;
gint row;
gboolean unselect;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CTREE (clist));
-
+
if (clist->selection_mode != GTK_SELECTION_EXTENDED)
return;
-
+
if (clist->anchor < 0 || clist->drag_pos < 0)
return;
-
+
ctree = GTK_CTREE (clist);
clist->freeze_count++;
-
+
i = MIN (clist->anchor, clist->drag_pos);
e = MAX (clist->anchor, clist->drag_pos);
-
+
if (clist->undo_selection)
{
list = clist->selection;
@@ -5661,14 +5662,14 @@ resync_selection (GtkCList *clist, GdkEvent *event)
clist->selection_end = g_list_last (clist->selection);
clist->undo_selection = list;
list = clist->selection;
-
+
while (list)
{
node = list->data;
list = list->next;
unselect = TRUE;
-
+
if (gtk_ctree_is_viewable (ctree, node))
{
row = g_list_position (clist->row_list, (GList *)node);
@@ -5684,7 +5685,7 @@ resync_selection (GtkCList *clist, GdkEvent *event)
}
}
}
-
+
if (clist->anchor < clist->drag_pos)
{
for (node = GTK_CTREE_NODE (g_list_nth (clist->row_list, i)); i <= e;
@@ -5733,14 +5734,14 @@ resync_selection (GtkCList *clist, GdkEvent *event)
}
}
}
-
+
clist->undo_unselection = g_list_reverse (clist->undo_unselection);
for (list = clist->undo_unselection; list; list = list->next)
gtk_ctree_select (ctree, list->data);
-
+
clist->anchor = -1;
clist->drag_pos = -1;
-
+
if (!CLIST_UNFROZEN (clist))
clist->freeze_count--;
}
@@ -5750,29 +5751,29 @@ real_undo_selection (GtkCList *clist)
{
GtkCTree *ctree;
GList *work;
-
+
g_return_if_fail (clist != NULL);
g_return_if_fail (GTK_IS_CTREE (clist));
-
+
if (clist->selection_mode != GTK_SELECTION_EXTENDED)
return;
-
+
if (!(clist->undo_selection || clist->undo_unselection))
{
gtk_clist_unselect_all (clist);
return;
}
-
+
ctree = GTK_CTREE (clist);
-
+
for (work = clist->undo_selection; work; work = work->next)
if (GTK_CTREE_ROW (work->data)->row.selectable)
gtk_ctree_select (ctree, GTK_CTREE_NODE (work->data));
-
+
for (work = clist->undo_unselection; work; work = work->next)
if (GTK_CTREE_ROW (work->data)->row.selectable)
gtk_ctree_unselect (ctree, GTK_CTREE_NODE (work->data));
-
+
if (GTK_WIDGET_HAS_FOCUS (clist) && clist->focus_row != clist->undo_anchor)
{
gtk_widget_draw_focus (GTK_WIDGET (clist));
@@ -5783,18 +5784,18 @@ real_undo_selection (GtkCList *clist)
clist->focus_row = clist->undo_anchor;
clist->undo_anchor = -1;
-
+
g_list_free (clist->undo_selection);
g_list_free (clist->undo_unselection);
clist->undo_selection = NULL;
clist->undo_unselection = NULL;
-
+
if (ROW_TOP_YPIXEL (clist, clist->focus_row) + clist->row_height >
clist->clist_window_height)
gtk_clist_moveto (clist, clist->focus_row, -1, 1, 0);
else if (ROW_TOP_YPIXEL (clist, clist->focus_row) < 0)
gtk_clist_moveto (clist, clist->focus_row, -1, 0, 0);
-
+
}
void
@@ -5803,7 +5804,7 @@ gtk_ctree_set_drag_compare_func (GtkCTree *ctree,
{
g_return_if_fail (ctree != NULL);
g_return_if_fail (GTK_IS_CTREE (ctree));
-
+
ctree->drag_compare = cmp_func;
}
@@ -5815,7 +5816,7 @@ check_drag (GtkCTree *ctree,
{
g_return_val_if_fail (ctree != NULL, FALSE);
g_return_val_if_fail (GTK_IS_CTREE (ctree), FALSE);
-
+
if (drag_source && drag_source != drag_target &&
(!GTK_CTREE_ROW (drag_source)->children ||
!gtk_ctree_is_ancestor (ctree, drag_source, drag_target)))
@@ -5861,7 +5862,7 @@ static void
drag_dest_info_destroy (gpointer data)
{
GtkCListDestInfo *info = data;
-
+
g_free (info);
}
@@ -5872,15 +5873,15 @@ drag_dest_cell (GtkCList *clist,
GtkCListDestInfo *dest_info)
{
GtkWidget *widget;
-
+
widget = GTK_WIDGET (clist);
-
+
dest_info->insert_pos = GTK_CLIST_DRAG_NONE;
-
+
y -= (GTK_CONTAINER (widget)->border_width +
widget->style->klass->ythickness + clist->column_title_area.height);
dest_info->cell.row = ROW_FROM_YPIXEL (clist, y);
-
+
if (dest_info->cell.row >= clist->rows)
{
dest_info->cell.row = clist->rows - 1;
@@ -5888,15 +5889,15 @@ drag_dest_cell (GtkCList *clist,
}
if (dest_info->cell.row < -1)
dest_info->cell.row = -1;
-
+
x -= GTK_CONTAINER (widget)->border_width + widget->style->klass->xthickness;
dest_info->cell.column = COLUMN_FROM_XPIXEL (clist, x);
-
+
if (dest_info->cell.row >= 0)
{
gint y_delta;
gint h = 0;
-
+
y_delta = y - ROW_TOP_YPIXEL (clist, dest_info->cell.row);
if (GTK_CLIST_DRAW_DRAG_RECT(clist) &&
@@ -5911,7 +5912,7 @@ drag_dest_cell (GtkCList *clist,
dest_info->insert_pos = GTK_CLIST_DRAG_BEFORE;
h = clist->row_height / 2;
}
-
+
if (GTK_CLIST_DRAW_DRAG_LINE(clist))
{
if (y_delta < h)
@@ -5929,22 +5930,22 @@ gtk_ctree_drag_begin (GtkWidget *widget,
GtkCList *clist;
GtkCTree *ctree;
gboolean use_icons;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CTREE (widget));
g_return_if_fail (context != NULL);
-
+
clist = GTK_CLIST (widget);
ctree = GTK_CTREE (widget);
-
+
use_icons = GTK_CLIST_USE_DRAG_ICONS (clist);
GTK_CLIST_UNSET_FLAG (clist, CLIST_USE_DRAG_ICONS);
GTK_WIDGET_CLASS (parent_class)->drag_begin (widget, context);
-
+
if (use_icons)
{
GtkCTreeNode *node;
-
+
GTK_CLIST_SET_FLAG (clist, CLIST_USE_DRAG_ICONS);
node = GTK_CTREE_NODE (g_list_nth (clist->row_list,
clist->click_cell.row));
@@ -5979,34 +5980,34 @@ gtk_ctree_drag_motion (GtkWidget *widget,
GtkCTree *ctree;
GtkCListDestInfo new_info;
GtkCListDestInfo *dest_info;
-
+
g_return_val_if_fail (widget != NULL, FALSE);
g_return_val_if_fail (GTK_IS_CTREE (widget), FALSE);
-
+
clist = GTK_CLIST (widget);
ctree = GTK_CTREE (widget);
-
+
dest_info = g_dataset_get_data (context, "gtk-clist-drag-dest");
-
+
if (!dest_info)
{
dest_info = g_new (GtkCListDestInfo, 1);
-
+
dest_info->cell.row = -1;
dest_info->cell.column = -1;
dest_info->insert_pos = GTK_CLIST_DRAG_NONE;
-
+
g_dataset_set_data_full (context, "gtk-clist-drag-dest", dest_info,
drag_dest_info_destroy);
}
-
+
drag_dest_cell (clist, x, y, &new_info);
-
+
if (GTK_CLIST_REORDERABLE (clist))
{
GList *list;
GdkAtom atom = gdk_atom_intern ("gtk-clist-drag-reorder", FALSE);
-
+
list = context->targets;
while (list)
{
@@ -6014,17 +6015,17 @@ gtk_ctree_drag_motion (GtkWidget *widget,
break;
list = list->next;
}
-
+
if (list)
{
GtkCTreeNode *drag_source;
GtkCTreeNode *drag_target;
-
+
drag_source = GTK_CTREE_NODE (g_list_nth (clist->row_list,
clist->click_cell.row));
drag_target = GTK_CTREE_NODE (g_list_nth (clist->row_list,
new_info.cell.row));
-
+
if (gtk_drag_get_source_widget (context) != widget ||
!check_drag (ctree, drag_source, drag_target,
new_info.insert_pos))
@@ -6036,7 +6037,7 @@ gtk_ctree_drag_motion (GtkWidget *widget,
}
return TRUE;
}
-
+
if (new_info.cell.row != dest_info->cell.row ||
(new_info.cell.row == dest_info->cell.row &&
dest_info->insert_pos != new_info.insert_pos))
@@ -6046,22 +6047,22 @@ gtk_ctree_drag_motion (GtkWidget *widget,
(clist,
g_list_nth (clist->row_list, dest_info->cell.row)->data,
dest_info->cell.row, dest_info->insert_pos);
-
+
dest_info->insert_pos = new_info.insert_pos;
dest_info->cell.row = new_info.cell.row;
dest_info->cell.column = new_info.cell.column;
-
+
GTK_CLIST_GET_CLASS (clist)->draw_drag_highlight
(clist,
g_list_nth (clist->row_list, dest_info->cell.row)->data,
dest_info->cell.row, dest_info->insert_pos);
-
+
gdk_drag_status (context, context->suggested_action, time);
}
return TRUE;
}
}
-
+
dest_info->insert_pos = new_info.insert_pos;
dest_info->cell.row = new_info.cell.row;
dest_info->cell.column = new_info.cell.column;
@@ -6079,15 +6080,15 @@ gtk_ctree_drag_data_received (GtkWidget *widget,
{
GtkCTree *ctree;
GtkCList *clist;
-
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_CTREE (widget));
g_return_if_fail (context != NULL);
g_return_if_fail (selection_data != NULL);
-
+
ctree = GTK_CTREE (widget);
clist = GTK_CLIST (widget);
-
+
if (GTK_CLIST_REORDERABLE (clist) &&
gtk_drag_get_source_widget (context) == widget &&
selection_data->target ==
@@ -6096,24 +6097,24 @@ gtk_ctree_drag_data_received (GtkWidget *widget,
selection_data->length == sizeof (GtkCListCellInfo))
{
GtkCListCellInfo *source_info;
-
+
source_info = (GtkCListCellInfo *)(selection_data->data);
if (source_info)
{
GtkCListDestInfo dest_info;
GtkCTreeNode *source_node;
GtkCTreeNode *dest_node;
-
+
drag_dest_cell (clist, x, y, &dest_info);
source_node = GTK_CTREE_NODE (g_list_nth (clist->row_list,
source_info->row));
dest_node = GTK_CTREE_NODE (g_list_nth (clist->row_list,
dest_info.cell.row));
-
+
if (!source_node || !dest_node)
return;
-
+
switch (dest_info.insert_pos)
{
case GTK_CLIST_DRAG_NONE:
diff --git a/gtk/gtkdebug.h b/gtk/gtkdebug.h
index ad1035ca4..81d0d0828 100644
--- a/gtk/gtkdebug.h
+++ b/gtk/gtkdebug.h
@@ -36,7 +36,8 @@ typedef enum {
GTK_DEBUG_MISC = 1 << 1,
GTK_DEBUG_SIGNALS = 1 << 2,
GTK_DEBUG_DND = 1 << 3,
- GTK_DEBUG_PLUGSOCKET = 1 << 4
+ GTK_DEBUG_PLUGSOCKET = 1 << 4,
+ GTK_DEBUG_TEXT = 1 << 5
} GtkDebugFlag;
#ifdef G_ENABLE_DEBUG
diff --git a/gtk/gtkeditable.c b/gtk/gtkeditable.c
index 87bfe85fb..107bea084 100644
--- a/gtk/gtkeditable.c
+++ b/gtk/gtkeditable.c
@@ -424,6 +424,9 @@ gtk_editable_insert_text (GtkEditable *editable,
klass = GTK_EDITABLE_GET_CLASS (editable);
+ if (new_text_length < 0)
+ new_text_length = strlen (new_text);
+
if (new_text_length <= 64)
text = buf;
else
@@ -965,32 +968,3 @@ gtk_editable_changed (GtkEditable *editable)
gtk_signal_emit (GTK_OBJECT (editable), editable_signals[CHANGED]);
}
-
-#if 0
-static void
-gtk_editable_parent_set (GtkWidget *widget,
- GtkWidget *old_parent,
- GtkWidget *editable)
-{
- GtkWidget *parent;
-
- parent = old_parent;
- while (parent)
- {
- gtk_signal_disconnect_by_func (GTK_OBJECT (parent),
- GTK_SIGNAL_FUNC (gtk_editable_parent_set),
- editable);
- parent = parent->parent;
- }
-
- parent = widget->parent;
- while (parent)
- {
- gtk_signal_connect (GTK_OBJECT (parent), "parent_set",
- GTK_SIGNAL_FUNC (gtk_editable_parent_set),
- editable);
-
- parent = parent->parent;
- }
-}
-#endif
diff --git a/gtk/gtkentry.c b/gtk/gtkentry.c
index 3f9eec91b..1f11a5a0c 100644
--- a/gtk/gtkentry.c
+++ b/gtk/gtkentry.c
@@ -29,17 +29,26 @@
#include "gdk/gdkkeysyms.h"
#include "gdk/gdki18n.h"
#include "gtkentry.h"
+#include "gtkimmulticontext.h"
#include "gtkmain.h"
#include "gtkselection.h"
#include "gtksignal.h"
#include "gtkstyle.h"
+#include <pango/pango.h>
+#include <unicode.h>
+#include <glib-object.h>
+
#define MIN_ENTRY_WIDTH 150
#define DRAW_TIMEOUT 20
-
-/* If you are going to change this, see the note in entry_adjust_scroll */
#define INNER_BORDER 2
+/* Initial size of buffer, in bytes */
+#define MIN_SIZE 16
+
+/* Maximum size of text buffer, in bytes */
+#define MAX_SIZE G_MAXUSHORT
+
enum {
ARG_0,
ARG_MAX_LENGTH,
@@ -47,71 +56,68 @@ enum {
};
-static void gtk_entry_class_init (GtkEntryClass *klass);
-static void gtk_entry_init (GtkEntry *entry);
-static void gtk_entry_set_arg (GtkObject *object,
- GtkArg *arg,
- guint arg_id);
-static void gtk_entry_get_arg (GtkObject *object,
- GtkArg *arg,
- guint arg_id);
-static void gtk_entry_finalize (GObject *object);
-static void gtk_entry_realize (GtkWidget *widget);
-static void gtk_entry_unrealize (GtkWidget *widget);
-static void gtk_entry_draw_focus (GtkWidget *widget);
-static void gtk_entry_size_request (GtkWidget *widget,
- GtkRequisition *requisition);
-static void gtk_entry_size_allocate (GtkWidget *widget,
- GtkAllocation *allocation);
-static void gtk_entry_make_backing_pixmap (GtkEntry *entry,
- gint width, gint height);
-static void gtk_entry_draw (GtkWidget *widget,
- GdkRectangle *area);
-static gint gtk_entry_expose (GtkWidget *widget,
- GdkEventExpose *event);
-static gint gtk_entry_button_press (GtkWidget *widget,
- GdkEventButton *event);
-static gint gtk_entry_button_release (GtkWidget *widget,
- GdkEventButton *event);
-static gint gtk_entry_motion_notify (GtkWidget *widget,
- GdkEventMotion *event);
-static gint gtk_entry_key_press (GtkWidget *widget,
- GdkEventKey *event);
-static gint gtk_entry_focus_in (GtkWidget *widget,
- GdkEventFocus *event);
-static gint gtk_entry_focus_out (GtkWidget *widget,
- GdkEventFocus *event);
-static void gtk_entry_draw_text (GtkEntry *entry);
-static void gtk_entry_draw_cursor (GtkEntry *entry);
-static void gtk_entry_draw_cursor_on_drawable
- (GtkEntry *entry,
- GdkDrawable *drawable);
-static void gtk_entry_style_set (GtkWidget *widget,
- GtkStyle *previous_style);
-static void gtk_entry_state_changed (GtkWidget *widget,
- GtkStateType previous_state);
-#ifdef USE_XIM
-static void gtk_entry_update_ic_attr (GtkWidget *widget);
-#endif
-static void gtk_entry_queue_draw (GtkEntry *entry);
-static gint gtk_entry_timer (gpointer data);
-static gint gtk_entry_position (GtkEntry *entry,
- gint x);
-static void entry_adjust_scroll (GtkEntry *entry);
-static void gtk_entry_grow_text (GtkEntry *entry);
-static void gtk_entry_insert_text (GtkEditable *editable,
- const gchar *new_text,
- gint new_text_length,
- gint *position);
-static void gtk_entry_delete_text (GtkEditable *editable,
- gint start_pos,
- gint end_pos);
-static void gtk_entry_update_text (GtkEditable *editable,
- gint start_pos,
- gint end_pos);
-static gchar *gtk_entry_get_chars (GtkEditable *editable,
- gint start_pos,
- gint end_pos);
+static void gtk_entry_class_init (GtkEntryClass *klass);
+static void gtk_entry_init (GtkEntry *entry);
+static void gtk_entry_set_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id);
+static void gtk_entry_get_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id);
+static void gtk_entry_finalize (GObject *object);
+static void gtk_entry_realize (GtkWidget *widget);
+static void gtk_entry_unrealize (GtkWidget *widget);
+static void gtk_entry_draw_focus (GtkWidget *widget);
+static void gtk_entry_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void gtk_entry_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static void gtk_entry_draw (GtkWidget *widget,
+ GdkRectangle *area);
+static gint gtk_entry_expose (GtkWidget *widget,
+ GdkEventExpose *event);
+static gint gtk_entry_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static gint gtk_entry_button_release (GtkWidget *widget,
+ GdkEventButton *event);
+static gint gtk_entry_motion_notify (GtkWidget *widget,
+ GdkEventMotion *event);
+static gint gtk_entry_key_press (GtkWidget *widget,
+ GdkEventKey *event);
+static gint gtk_entry_focus_in (GtkWidget *widget,
+ GdkEventFocus *event);
+static gint gtk_entry_focus_out (GtkWidget *widget,
+ GdkEventFocus *event);
+static void gtk_entry_draw_text (GtkEntry *entry);
+static void gtk_entry_ensure_layout (GtkEntry *entry);
+static void gtk_entry_draw_cursor (GtkEntry *entry);
+static void gtk_entry_style_set (GtkWidget *widget,
+ GtkStyle *previous_style);
+static void gtk_entry_state_changed (GtkWidget *widget,
+ GtkStateType previous_state);
+static void gtk_entry_queue_draw (GtkEntry *entry);
+static gint gtk_entry_find_position (GtkEntry *entry,
+ gint x);
+static void gtk_entry_get_cursor_locations (GtkEntry *entry,
+ gint *strong_x,
+ gint *weak_x);
+static void entry_adjust_scroll (GtkEntry *entry);
+static void gtk_entry_insert_text (GtkEditable *editable,
+ const gchar *new_text,
+ gint new_text_length,
+ gint *position);
+static void gtk_entry_delete_text (GtkEditable *editable,
+ gint start_pos,
+ gint end_pos);
+static void gtk_entry_update_text (GtkEditable *editable,
+ gint start_pos,
+ gint end_pos);
+static gchar * gtk_entry_get_chars (GtkEditable *editable,
+ gint start_pos,
+ gint end_pos);
+
+
+
/* Binding actions */
static void gtk_entry_move_cursor (GtkEditable *editable,
@@ -151,12 +157,14 @@ static void gtk_entry_set_selection (GtkEditable *editable,
gint start,
gint end);
-static void gtk_entry_recompute_offsets (GtkEntry *entry);
-static gint gtk_entry_find_position (GtkEntry *entry,
- gint position);
static void gtk_entry_set_position_from_editable (GtkEditable *editable,
gint position);
+static void gtk_entry_commit_cb (GtkIMContext *context,
+ const gchar *str,
+ GtkEntry *entry);
+
+
static GtkWidgetClass *parent_class = NULL;
static GdkAtom ctext_atom = GDK_NONE;
@@ -352,22 +360,26 @@ gtk_entry_init (GtkEntry *entry)
GTK_WIDGET_SET_FLAGS (entry, GTK_CAN_FOCUS);
entry->text_area = NULL;
- entry->backing_pixmap = NULL;
- entry->text = NULL;
- entry->text_size = 0;
+
+ entry->text_size = MIN_SIZE;
+ entry->text = g_malloc (entry->text_size);
+ entry->text[0] = '\0';
+
entry->text_length = 0;
entry->text_max_length = 0;
+ entry->n_bytes = 0;
entry->scroll_offset = 0;
entry->timer = 0;
entry->button = 0;
- entry->visible = 1;
+ entry->ascent = 0;
- entry->char_offset = NULL;
- entry->text_mb = NULL;
- entry->text_mb_dirty = TRUE;
- entry->use_wchar = FALSE;
-
- gtk_entry_grow_text (entry);
+ /* This object is completely private. No external entity can gain a reference
+ * to it; so we create it here and destroy it in finalize().
+ */
+ entry->im_context = gtk_im_multicontext_new ();
+
+ gtk_signal_connect (GTK_OBJECT (entry->im_context), "commit",
+ GTK_SIGNAL_FUNC (gtk_entry_commit_cb), entry);
}
GtkWidget*
@@ -406,12 +418,6 @@ gtk_entry_set_text (GtkEntry *entry,
tmp_pos = 0;
gtk_editable_insert_text (editable, text, strlen (text), &tmp_pos);
editable->current_pos = tmp_pos;
-
- editable->selection_start_pos = 0;
- editable->selection_end_pos = 0;
-
- if (GTK_WIDGET_DRAWABLE (entry))
- gtk_entry_draw_text (entry);
}
void
@@ -426,7 +432,6 @@ gtk_entry_append_text (GtkEntry *entry,
tmp_pos = entry->text_length;
gtk_editable_insert_text (GTK_EDITABLE(entry), text, strlen (text), &tmp_pos);
- GTK_EDITABLE(entry)->current_pos = tmp_pos;
}
void
@@ -441,7 +446,6 @@ gtk_entry_prepend_text (GtkEntry *entry,
tmp_pos = 0;
gtk_editable_insert_text (GTK_EDITABLE(entry), text, strlen (text), &tmp_pos);
- GTK_EDITABLE(entry)->current_pos = tmp_pos;
}
void
@@ -472,10 +476,9 @@ gtk_entry_set_visibility (GtkEntry *entry,
g_return_if_fail (entry != NULL);
g_return_if_fail (GTK_IS_ENTRY (entry));
- entry->visible = visible ? TRUE : FALSE;
GTK_EDITABLE (entry)->visible = visible ? TRUE : FALSE;
- gtk_entry_recompute_offsets (entry);
- gtk_widget_queue_draw (GTK_WIDGET (entry));
+
+ gtk_entry_queue_draw (entry);
}
void
@@ -494,24 +497,7 @@ gtk_entry_get_text (GtkEntry *entry)
g_return_val_if_fail (entry != NULL, NULL);
g_return_val_if_fail (GTK_IS_ENTRY (entry), NULL);
- if (!entry->text_mb_dirty)
- return entry->text_mb;
-
- if (entry->text_mb)
- g_free(entry->text_mb);
-
- if (!entry->text)
- {
- entry->text_mb = g_new(gchar, 1);
- entry->text_mb[0] = 0;
- }
- else
- {
- entry->text_mb = gtk_entry_get_chars(GTK_EDITABLE(entry), 0, -1);
- }
- entry->text_mb_dirty = 0;
-
- return entry->text_mb;
+ return entry->text;
}
static void
@@ -523,6 +509,8 @@ gtk_entry_finalize (GObject *object)
entry = GTK_ENTRY (object);
+ gtk_object_unref (GTK_OBJECT (entry->im_context));
+
if (entry->timer)
gtk_timeout_remove (entry->timer);
@@ -530,17 +518,8 @@ gtk_entry_finalize (GObject *object)
if (entry->text)
g_free (entry->text);
- if (entry->char_offset)
- g_free (entry->char_offset);
entry->text = NULL;
- if (entry->text_mb)
- g_free (entry->text_mb);
- entry->text_mb = NULL;
-
- if (entry->backing_pixmap)
- gdk_pixmap_unref (entry->backing_pixmap);
-
G_OBJECT_CLASS (parent_class)->finalize (object);
}
@@ -601,82 +580,12 @@ gtk_entry_realize (GtkWidget *widget)
gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
gdk_window_set_background (entry->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
-#ifdef USE_XIM
- if (gdk_im_ready () && (editable->ic_attr = gdk_ic_attr_new ()) != NULL)
- {
- gint width, height;
- GdkEventMask mask;
- GdkColormap *colormap;
- GdkICAttr *attr = editable->ic_attr;
- GdkICAttributesType attrmask = GDK_IC_ALL_REQ;
- GdkIMStyle style;
- GdkIMStyle supported_style = GDK_IM_PREEDIT_NONE |
- GDK_IM_PREEDIT_NOTHING |
- GDK_IM_PREEDIT_POSITION |
- GDK_IM_STATUS_NONE |
- GDK_IM_STATUS_NOTHING;
-
- if (widget->style && widget->style->font->type != GDK_FONT_FONTSET)
- supported_style &= ~GDK_IM_PREEDIT_POSITION;
-
- attr->style = style = gdk_im_decide_style (supported_style);
- attr->client_window = entry->text_area;
-
- if ((colormap = gtk_widget_get_colormap (widget)) !=
- gtk_widget_get_default_colormap ())
- {
- attrmask |= GDK_IC_PREEDIT_COLORMAP;
- attr->preedit_colormap = colormap;
- }
- attrmask |= GDK_IC_PREEDIT_FOREGROUND;
- attrmask |= GDK_IC_PREEDIT_BACKGROUND;
- attr->preedit_foreground = widget->style->fg[GTK_STATE_NORMAL];
- attr->preedit_background = widget->style->base[GTK_STATE_NORMAL];
-
- switch (style & GDK_IM_PREEDIT_MASK)
- {
- case GDK_IM_PREEDIT_POSITION:
- if (widget->style && widget->style->font->type != GDK_FONT_FONTSET)
- {
- g_warning ("over-the-spot style requires fontset");
- break;
- }
-
- gdk_window_get_size (entry->text_area, &width, &height);
-
- attrmask |= GDK_IC_PREEDIT_POSITION_REQ;
- attr->spot_location.x = 0;
- attr->spot_location.y = height;
- attr->preedit_area.x = 0;
- attr->preedit_area.y = 0;
- attr->preedit_area.width = width;
- attr->preedit_area.height = height;
- attr->preedit_fontset = widget->style->font;
-
- break;
- }
- editable->ic = gdk_ic_new (attr, attrmask);
-
- if (editable->ic == NULL)
- g_warning ("Can't create input context.");
- else
- {
- mask = gdk_window_get_events (entry->text_area);
- mask |= gdk_ic_get_events (editable->ic);
- gdk_window_set_events (entry->text_area, mask);
-
- if (GTK_WIDGET_HAS_FOCUS(widget))
- gdk_im_begin (editable->ic, entry->text_area);
- }
- }
-#endif
-
gdk_window_show (entry->text_area);
if (editable->selection_start_pos != editable->selection_end_pos)
gtk_editable_claim_selection (editable, TRUE, GDK_CURRENT_TIME);
- gtk_entry_recompute_offsets (entry);
+ gtk_im_context_set_client_window (entry->im_context, entry->text_area);
}
static void
@@ -689,19 +598,8 @@ gtk_entry_unrealize (GtkWidget *widget)
entry = GTK_ENTRY (widget);
-#ifdef USE_XIM
- if (GTK_EDITABLE (widget)->ic)
- {
- gdk_ic_destroy (GTK_EDITABLE (widget)->ic);
- GTK_EDITABLE (widget)->ic = NULL;
- }
- if (GTK_EDITABLE (widget)->ic_attr)
- {
- gdk_ic_attr_destroy (GTK_EDITABLE (widget)->ic_attr);
- GTK_EDITABLE (widget)->ic_attr = NULL;
- }
-#endif
-
+ gtk_im_context_set_client_window (entry->im_context, entry->text_area);
+
if (entry->text_area)
{
gdk_window_set_user_data (entry->text_area, NULL);
@@ -750,9 +648,6 @@ gtk_entry_draw_focus (GtkWidget *widget)
NULL, widget, "entry",
0, 0, width - 1, height - 1);
}
-
- if (GTK_EDITABLE (widget)->editable)
- gtk_entry_draw_cursor (GTK_ENTRY (widget));
}
}
@@ -760,13 +655,41 @@ static void
gtk_entry_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
+ GtkEntry *entry;
+ PangoFontMetrics metrics;
+ PangoFont *font;
+ gchar *lang;
+
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_ENTRY (widget));
g_return_if_fail (requisition != NULL);
+ entry = GTK_ENTRY (widget);
+
+ /* We do this to deal with direction changes - should that be a signal?
+ */
+ if (entry->layout)
+ {
+ pango_layout_unref (entry->layout);
+ entry->layout = NULL;
+ }
+
+ gtk_entry_ensure_layout (entry);
+
+ /* hackish for now, get metrics
+ */
+ font = pango_context_load_font (pango_layout_get_context (entry->layout),
+ widget->style->font_desc);
+ lang = pango_context_get_lang (pango_layout_get_context (entry->layout));
+ pango_font_get_metrics (font, lang, &metrics);
+ g_free (lang);
+
+ g_object_unref (G_OBJECT (font));
+
+ entry->ascent = metrics.ascent;
+
requisition->width = MIN_ENTRY_WIDTH + (widget->style->klass->xthickness + INNER_BORDER) * 2;
- requisition->height = (widget->style->font->ascent +
- widget->style->font->descent +
+ requisition->height = ((metrics.ascent + metrics.descent) / PANGO_SCALE +
(widget->style->klass->ythickness + INNER_BORDER) * 2);
}
@@ -804,23 +727,10 @@ gtk_entry_size_allocate (GtkWidget *widget,
allocation->width - widget->style->klass->xthickness * 2,
requisition.height - widget->style->klass->ythickness * 2);
- /* And make sure the cursor is on screen */
- entry_adjust_scroll (entry);
-
-#ifdef USE_XIM
- if (editable->ic &&
- (gdk_ic_get_style (editable->ic) & GDK_IM_PREEDIT_POSITION))
- {
- gint width, height;
-
- gdk_window_get_size (entry->text_area, &width, &height);
- editable->ic_attr->preedit_area.width = width;
- editable->ic_attr->preedit_area.height = height;
- gdk_ic_set_attr (editable->ic, editable->ic_attr,
- GDK_IC_PREEDIT_AREA);
- }
-#endif
}
+
+ /* And make sure the cursor is on screen */
+ entry_adjust_scroll (entry);
}
static void
@@ -845,6 +755,7 @@ gtk_entry_draw (GtkWidget *widget,
gdk_window_begin_paint_rect (entry->text_area, &tmp_area);
gtk_widget_draw_focus (widget);
gtk_entry_draw_text (GTK_ENTRY (widget));
+ gtk_entry_draw_cursor (GTK_ENTRY (widget));
gdk_window_end_paint (entry->text_area);
}
}
@@ -864,7 +775,10 @@ gtk_entry_expose (GtkWidget *widget,
if (widget->window == event->window)
gtk_widget_draw_focus (widget);
else if (entry->text_area == event->window)
- gtk_entry_draw_text (GTK_ENTRY (widget));
+ {
+ gtk_entry_draw_text (GTK_ENTRY (widget));
+ gtk_entry_draw_cursor (GTK_ENTRY (widget));
+ }
return FALSE;
}
@@ -902,7 +816,7 @@ gtk_entry_button_press (GtkWidget *widget,
case GDK_BUTTON_PRESS:
gtk_grab_add (widget);
- tmp_pos = gtk_entry_position (entry, event->x + entry->scroll_offset);
+ tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
/* Set it now, so we display things right. We'll unset it
* later if things don't work out */
editable->has_selection = TRUE;
@@ -930,7 +844,7 @@ gtk_entry_button_press (GtkWidget *widget,
{
if (editable->selection_start_pos == editable->selection_end_pos ||
editable->has_selection)
- editable->current_pos = gtk_entry_position (entry, event->x + entry->scroll_offset);
+ editable->current_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
gtk_selection_convert (widget, GDK_SELECTION_PRIMARY,
ctext_atom, event->time);
}
@@ -938,7 +852,7 @@ gtk_entry_button_press (GtkWidget *widget,
{
gtk_grab_add (widget);
- tmp_pos = gtk_entry_position (entry, event->x + entry->scroll_offset);
+ tmp_pos = gtk_entry_find_position (entry, event->x + entry->scroll_offset);
gtk_entry_set_selection (editable, tmp_pos, tmp_pos);
editable->has_selection = FALSE;
editable->current_pos = editable->selection_start_pos;
@@ -1024,7 +938,7 @@ gtk_entry_motion_notify (GtkWidget *widget,
if (event->is_hint || (entry->text_area != event->window))
gdk_window_get_pointer (entry->text_area, &x, NULL, NULL);
- GTK_EDITABLE(entry)->selection_end_pos = gtk_entry_position (entry, x + entry->scroll_offset);
+ GTK_EDITABLE(entry)->selection_end_pos = gtk_entry_find_position (entry, x + entry->scroll_offset);
GTK_EDITABLE(entry)->current_pos = GTK_EDITABLE(entry)->selection_end_pos;
entry_adjust_scroll (entry);
gtk_entry_queue_draw (entry);
@@ -1173,19 +1087,8 @@ gtk_entry_key_press (GtkWidget *widget,
break;
}
}
- if (event->length > 0)
- {
- gint tmp_pos;
-
- extend_selection = FALSE;
- gtk_editable_delete_selection (editable);
-
- tmp_pos = editable->current_pos;
- gtk_editable_insert_text (editable, event->string, event->length, &tmp_pos);
- editable->current_pos = tmp_pos;
-
- return_val = TRUE;
- }
+ gtk_im_context_filter_keypress (entry->im_context, event);
+
break;
}
@@ -1237,11 +1140,9 @@ gtk_entry_focus_in (GtkWidget *widget,
GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
gtk_widget_draw_focus (widget);
-
-#ifdef USE_XIM
- if (GTK_EDITABLE(widget)->ic)
- gdk_im_begin (GTK_EDITABLE(widget)->ic, GTK_ENTRY(widget)->text_area);
-#endif
+ gtk_entry_queue_draw (GTK_ENTRY (widget));
+
+ gtk_im_context_focus_in (GTK_ENTRY (widget)->im_context);
return FALSE;
}
@@ -1256,38 +1157,24 @@ gtk_entry_focus_out (GtkWidget *widget,
GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
gtk_widget_draw_focus (widget);
+ gtk_entry_queue_draw (GTK_ENTRY (widget));
-#ifdef USE_XIM
- gdk_im_end ();
-#endif
+ gtk_im_context_focus_out (GTK_ENTRY (widget)->im_context);
return FALSE;
}
static void
-gtk_entry_make_backing_pixmap (GtkEntry *entry, gint width, gint height)
+gtk_entry_ensure_layout (GtkEntry *entry)
{
- gint pixmap_width, pixmap_height;
+ GtkWidget *widget = GTK_WIDGET (entry);
+
+ PangoAttrList *attrs;
- if (!entry->backing_pixmap)
- {
- /* allocate */
- entry->backing_pixmap = gdk_pixmap_new (entry->text_area,
- width, height,
- -1);
- }
- else
+ if (!entry->layout)
{
- /* reallocate if sizes don't match */
- gdk_window_get_size (entry->backing_pixmap,
- &pixmap_width, &pixmap_height);
- if ((pixmap_width != width) || (pixmap_height != height))
- {
- gdk_pixmap_unref (entry->backing_pixmap);
- entry->backing_pixmap = gdk_pixmap_new (entry->text_area,
- width, height,
- -1);
- }
+ entry->layout = gtk_widget_create_pango_layout (widget);
+ pango_layout_set_text (entry->layout, entry->text, entry->n_bytes);
}
}
@@ -1295,131 +1182,72 @@ static void
gtk_entry_draw_text (GtkEntry *entry)
{
GtkWidget *widget;
- GtkEditable *editable;
- GtkStateType selected_state;
- gint start_pos;
- gint end_pos;
- gint start_xoffset;
- gint selection_start_pos;
- gint selection_end_pos;
- gint selection_start_xoffset;
- gint selection_end_xoffset;
- gint width, height;
- gint y;
- GdkDrawable *drawable;
- GdkWChar *stars;
- GdkWChar *toprint;
-
+ PangoLayoutLine *line;
+ GtkEditable *editable = GTK_EDITABLE (entry);
+
g_return_if_fail (entry != NULL);
g_return_if_fail (GTK_IS_ENTRY (entry));
- if (entry->timer)
- {
- gtk_timeout_remove (entry->timer);
- entry->timer = 0;
- }
-
if (GTK_WIDGET_DRAWABLE (entry))
{
- widget = GTK_WIDGET (entry);
- editable = GTK_EDITABLE (entry);
-
- if (!entry->text)
- {
- gtk_paint_flat_box (widget->style, entry->text_area,
- GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE,
- NULL, widget, "entry_bg",
- 0, 0, -1, -1);
-
- if (editable->editable)
- gtk_entry_draw_cursor (entry);
- return;
- }
-
- gdk_window_get_size (entry->text_area, &width, &height);
+ PangoRectangle logical_rect;
+ int area_height;
- gtk_paint_flat_box (widget->style, entry->text_area,
- GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE,
- NULL, widget, "entry_bg",
- 0, 0, width, height);
+ gdk_window_get_size (entry->text_area, NULL, &area_height);
+ area_height = PANGO_SCALE * (area_height - 2 * INNER_BORDER);
+
+ widget = GTK_WIDGET (entry);
- y = (height - (widget->style->font->ascent + widget->style->font->descent)) / 2;
- y += widget->style->font->ascent;
+ gtk_entry_ensure_layout (entry);
- start_pos = gtk_entry_find_position (entry, entry->scroll_offset);
- start_xoffset = entry->char_offset[start_pos] - entry->scroll_offset;
+ line = pango_layout_get_lines (entry->layout)->data;
+ pango_layout_line_get_extents (line, NULL, &logical_rect);
- end_pos = gtk_entry_find_position (entry, entry->scroll_offset + width);
- if (end_pos < entry->text_length)
- end_pos += 1;
+ gdk_draw_layout (entry->text_area, widget->style->text_gc [widget->state],
+ INNER_BORDER - entry->scroll_offset,
+ INNER_BORDER + ((area_height - logical_rect.height) / 2 +
+ entry->ascent + logical_rect.y) / PANGO_SCALE,
+ entry->layout);
- selected_state = GTK_STATE_SELECTED;
- if (!editable->has_selection)
- selected_state = GTK_STATE_ACTIVE;
+ if (editable->selection_start_pos != editable->selection_end_pos)
+ {
+ gint *ranges;
+ gint n_ranges, i;
+ gint start_index = unicode_offset_to_index (entry->text,
+ MIN (editable->selection_start_pos, editable->selection_end_pos));
+ gint end_index = unicode_offset_to_index (entry->text,
+ MAX (editable->selection_start_pos, editable->selection_end_pos));
+ GtkStateType selected_state = editable->has_selection ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
+ GdkRegion *clip_region = gdk_region_new ();
+
+ pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
+
+ for (i=0; i < n_ranges; i++)
+ {
+ GdkRectangle rect;
- selection_start_pos = MIN (editable->selection_start_pos, editable->selection_end_pos);
- selection_end_pos = MAX (editable->selection_start_pos, editable->selection_end_pos);
-
- selection_start_pos = CLAMP (selection_start_pos, start_pos, end_pos);
- selection_end_pos = CLAMP (selection_end_pos, start_pos, end_pos);
+ rect.x = INNER_BORDER - entry->scroll_offset + ranges[2*i] / PANGO_SCALE;
+ rect.y = INNER_BORDER + (entry->ascent + logical_rect.y) / PANGO_SCALE;
+ rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE;
+ rect.height = logical_rect.height / PANGO_SCALE;
+
+ gdk_draw_rectangle (entry->text_area, widget->style->bg_gc [selected_state], TRUE,
+ rect.x, rect.y, rect.width, rect.height);
- selection_start_xoffset =
- entry->char_offset[selection_start_pos] - entry->scroll_offset;
- selection_end_xoffset =
- entry->char_offset[selection_end_pos] -entry->scroll_offset;
+ gdk_region_union_with_rect (clip_region, &rect);
+ }
- /* if editable->visible, print a bunch of stars. If not, print the standard text. */
- if (editable->visible)
- {
- toprint = entry->text + start_pos;
- }
- else
- {
- gint i;
+ gdk_gc_set_clip_region (widget->style->fg_gc [selected_state], clip_region);
+ gdk_draw_layout (entry->text_area, widget->style->fg_gc [selected_state],
+ INNER_BORDER - entry->scroll_offset,
+ INNER_BORDER + ((area_height - logical_rect.height) / 2 +
+ entry->ascent + logical_rect.y) / PANGO_SCALE,
+ entry->layout);
+ gdk_gc_set_clip_region (widget->style->fg_gc [selected_state], NULL);
- stars = g_new (GdkWChar, end_pos - start_pos);
- for (i = 0; i < end_pos - start_pos; i++)
- stars[i] = '*';
- toprint = stars;
+ gdk_region_destroy (clip_region);
+ g_free (ranges);
}
-
- if (selection_start_pos > start_pos)
- gdk_draw_text_wc (entry->text_area, widget->style->font,
- widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
- INNER_BORDER + start_xoffset, y,
- toprint,
- selection_start_pos - start_pos);
-
- if ((selection_end_pos >= start_pos) &&
- (selection_start_pos < end_pos) &&
- (selection_start_pos != selection_end_pos))
- {
- gtk_paint_flat_box (widget->style, entry->text_area,
- selected_state, GTK_SHADOW_NONE,
- NULL, widget, "text",
- INNER_BORDER + selection_start_xoffset,
- INNER_BORDER,
- selection_end_xoffset - selection_start_xoffset,
- height - 2*INNER_BORDER);
- gdk_draw_text_wc (entry->text_area, widget->style->font,
- widget->style->fg_gc[selected_state],
- INNER_BORDER + selection_start_xoffset, y,
- toprint + selection_start_pos - start_pos,
- selection_end_pos - selection_start_pos);
- }
-
- if (selection_end_pos < end_pos)
- gdk_draw_text_wc (entry->text_area, widget->style->font,
- widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
- INNER_BORDER + selection_end_xoffset, y,
- toprint + selection_end_pos - start_pos,
- end_pos - selection_end_pos);
- /* free the space allocated for the stars if it's neccessary. */
- if (!editable->visible)
- g_free (toprint);
-
- if (editable->editable)
- gtk_entry_draw_cursor_on_drawable (entry, entry->text_area);
}
}
@@ -1429,89 +1257,32 @@ gtk_entry_draw_cursor (GtkEntry *entry)
g_return_if_fail (entry != NULL);
g_return_if_fail (GTK_IS_ENTRY (entry));
- gtk_entry_draw_cursor_on_drawable (entry, entry->text_area);
-}
-
-static void
-gtk_entry_draw_cursor_on_drawable (GtkEntry *entry, GdkDrawable *drawable)
-{
- GtkWidget *widget;
- GtkEditable *editable;
- gint xoffset;
- gint text_area_height;
-
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
-
if (GTK_WIDGET_DRAWABLE (entry))
{
- widget = GTK_WIDGET (entry);
- editable = GTK_EDITABLE (entry);
-
- xoffset = INNER_BORDER + entry->char_offset[editable->current_pos];
- xoffset -= entry->scroll_offset;
-
- gdk_window_get_size (entry->text_area, NULL, &text_area_height);
+ GtkWidget *widget = GTK_WIDGET (entry);
+ GtkEditable *editable = GTK_EDITABLE (entry);
if (GTK_WIDGET_HAS_FOCUS (widget) &&
(editable->selection_start_pos == editable->selection_end_pos))
{
- gdk_draw_line (drawable, widget->style->fg_gc[GTK_STATE_NORMAL],
- xoffset, INNER_BORDER,
- xoffset, text_area_height - INNER_BORDER);
- }
- else
- {
- int width, height;
- GdkRectangle area;
-
- gint yoffset =
- (text_area_height -
- (widget->style->font->ascent + widget->style->font->descent)) / 2
- + widget->style->font->ascent;
-
- area.x = xoffset;
- area.y = INNER_BORDER;
- area.width = 1;
- area.height = text_area_height - INNER_BORDER;
-
- gdk_window_get_size (entry->text_area, &width, &height);
-
- gtk_paint_flat_box (widget->style, drawable,
- GTK_WIDGET_STATE(widget), GTK_SHADOW_NONE,
- &area, widget, "entry_bg",
- 0, 0, width, height);
+ gint xoffset = INNER_BORDER - entry->scroll_offset;
+ gint strong_x, weak_x;
+ gint text_area_height;
-
- /* Draw the character under the cursor again
- */
- if ((editable->current_pos < entry->text_length) &&
- (editable->selection_start_pos == editable->selection_end_pos))
- {
- GdkWChar c = editable->visible ?
- *(entry->text + editable->current_pos) :
- '*';
-
- gdk_draw_text_wc (drawable, widget->style->font,
- widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
- xoffset, yoffset, &c, 1);
- }
- }
+ gdk_window_get_size (entry->text_area, NULL, &text_area_height);
+ gtk_entry_get_cursor_locations (entry, &strong_x, &weak_x);
-#ifdef USE_XIM
- if (GTK_WIDGET_HAS_FOCUS(widget) && gdk_im_ready() && editable->ic &&
- (gdk_ic_get_style (editable->ic) & GDK_IM_PREEDIT_POSITION))
- {
- editable->ic_attr->spot_location.x = xoffset;
- editable->ic_attr->spot_location.y =
- (text_area_height + (widget->style->font->ascent
- - widget->style->font->descent) + 1) / 2;
+ gdk_draw_line (entry->text_area, widget->style->bg_gc[GTK_STATE_SELECTED],
+ xoffset + strong_x, INNER_BORDER,
+ xoffset + strong_x, text_area_height - INNER_BORDER);
+
+ if (weak_x != strong_x)
+ gdk_draw_line (entry->text_area, widget->style->fg_gc[GTK_STATE_NORMAL],
+ xoffset + weak_x, INNER_BORDER,
+ xoffset + weak_x, text_area_height - INNER_BORDER);
- gdk_ic_set_attr (editable->ic,
- editable->ic_attr, GDK_IC_SPOT_LOCATION);
}
-#endif
}
}
@@ -1521,10 +1292,16 @@ gtk_entry_queue_draw (GtkEntry *entry)
g_return_if_fail (entry != NULL);
g_return_if_fail (GTK_IS_ENTRY (entry));
- if (!entry->timer)
- entry->timer = gtk_timeout_add (DRAW_TIMEOUT, gtk_entry_timer, entry);
+ if (GTK_WIDGET_REALIZED (entry))
+ {
+ GdkRectangle rect = { 0 };
+
+ gdk_window_get_size (entry->text_area, &rect.width, &rect.height);
+ gdk_window_invalidate_rect (entry->text_area, &rect, 0);
+ }
}
+#if 0
static gint
gtk_entry_timer (gpointer data)
{
@@ -1534,71 +1311,94 @@ gtk_entry_timer (gpointer data)
entry = GTK_ENTRY (data);
entry->timer = 0;
- gtk_entry_draw_text (entry);
GDK_THREADS_LEAVE ();
return FALSE;
}
+#endif
static gint
gtk_entry_find_position (GtkEntry *entry,
gint x)
{
- gint start = 0;
- gint end = entry->text_length;
- gint half;
-
- if (x <= 0)
- return 0;
- if (x >= entry->char_offset[end])
- return end;
+ PangoLayoutLine *line;
+ gint index;
+ gboolean trailing;
- /* invariant - char_offset[start] <= x < char_offset[end] */
+ gtk_entry_ensure_layout (entry);
- while (start != end)
- {
- half = (start+end)/2;
- if (half == start)
- return half;
- else if (entry->char_offset[half] <= x)
- start = half;
- else
- end = half;
- }
+ line = pango_layout_get_lines (entry->layout)->data;
+ pango_layout_line_x_to_index (line, x * PANGO_SCALE, &index, &trailing);
- return start;
+ if (trailing)
+ index = unicode_next_utf8 (entry->text + index) - entry->text;
+
+ return index;
}
-static gint
-gtk_entry_position (GtkEntry *entry,
- gint x)
+static void
+gtk_entry_get_cursor_locations (GtkEntry *entry,
+ gint *strong_x,
+ gint *weak_x)
{
- return gtk_entry_find_position(entry, x);
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ int index;
+
+ PangoRectangle strong_pos, weak_pos;
+
+ gtk_entry_ensure_layout (entry);
+
+ index = unicode_offset_to_index (entry->text, editable->current_pos);
+ pango_layout_get_cursor_pos (entry->layout, index, &strong_pos, &weak_pos);
+
+ if (strong_x)
+ *strong_x = strong_pos.x / PANGO_SCALE;
+
+ if (weak_x)
+ *weak_x = weak_pos.x / PANGO_SCALE;
}
static void
entry_adjust_scroll (GtkEntry *entry)
{
- gint xoffset, max_offset;
+ GtkWidget *widget;
+ gint min_offset, max_offset;
gint text_area_width;
+ gint strong_x, weak_x;
+ gint strong_xoffset, weak_xoffset;
+ PangoLayoutLine *line;
+ PangoRectangle logical_rect;
g_return_if_fail (entry != NULL);
g_return_if_fail (GTK_IS_ENTRY (entry));
- if (!entry->text_area)
+ widget = GTK_WIDGET (entry);
+ text_area_width = widget->allocation.width - 2 * (widget->style->klass->xthickness + INNER_BORDER);
+
+ if (!entry->layout)
return;
+
+ line = pango_layout_get_lines (entry->layout)->data;
+
+ /* Display as much text as we can */
- gdk_window_get_size (entry->text_area, &text_area_width, NULL);
- text_area_width -= 2 * INNER_BORDER;
+ pango_layout_line_get_extents (line, NULL, &logical_rect);
- /* Display as much text as we can */
- max_offset = MAX(0, entry->char_offset[entry->text_length] - text_area_width);
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+ {
+ min_offset = 0;
+ max_offset = MAX (min_offset, logical_rect.width / PANGO_SCALE - text_area_width);
+ }
+ else
+ {
+ max_offset = logical_rect.width / PANGO_SCALE - text_area_width;
+ min_offset = MIN (0, max_offset);
+ }
- if (entry->scroll_offset > max_offset)
- entry->scroll_offset = max_offset;
+ entry->scroll_offset = CLAMP (entry->scroll_offset, min_offset, max_offset);
- /* And make sure cursor is on screen. Note that the cursor is
+ /* And make sure cursors are on screen. Note that the cursor is
* actually drawn one pixel into the INNER_BORDER space on
* the right, when the scroll is at the utmost right. This
* looks better to to me than confining the cursor inside the
@@ -1607,43 +1407,39 @@ entry_adjust_scroll (GtkEntry *entry)
* on the left. This might need changing if one changed
* INNER_BORDER from 2 to 1, as one would do on a
* small-screen-real-estate display.
+ *
+ * We always make sure that the strong cursor is on screen, and
+ * put the weak cursor on screen if possible.
*/
- xoffset = entry->char_offset[GTK_EDITABLE(entry)->current_pos];
- xoffset -= entry->scroll_offset;
-
- if (xoffset < 0)
- entry->scroll_offset += xoffset;
- else if (xoffset > text_area_width)
- entry->scroll_offset += xoffset - text_area_width;
-
- gtk_widget_queue_draw (GTK_WIDGET (entry));
-}
-static void
-gtk_entry_grow_text (GtkEntry *entry)
-{
- gint previous_size;
- gint i;
+ gtk_entry_get_cursor_locations (entry, &strong_x, &weak_x);
+
+ strong_xoffset = strong_x - entry->scroll_offset;
- g_return_if_fail (entry != NULL);
- g_return_if_fail (GTK_IS_ENTRY (entry));
+ if (strong_xoffset < 0)
+ {
+ entry->scroll_offset += strong_xoffset;
+ strong_xoffset = 0;
+ }
+ else if (strong_xoffset > text_area_width)
+ {
+ entry->scroll_offset += strong_xoffset - text_area_width;
+ strong_xoffset = text_area_width;
+ }
- previous_size = entry->text_size;
- if (!entry->text_size)
- entry->text_size = 128;
- else
- entry->text_size *= 2;
- entry->text = g_realloc (entry->text, entry->text_size * sizeof(GdkWChar));
- entry->char_offset = g_realloc (entry->char_offset,
- entry->text_size * sizeof(guint));
+ weak_xoffset = weak_x - entry->scroll_offset;
- if (entry->text_length == 0) /* initial allocation */
+ if (weak_xoffset < 0 && strong_xoffset - weak_xoffset <= text_area_width)
{
- entry->char_offset[0] = 0;
+ entry->scroll_offset += weak_xoffset;
+ }
+ else if (weak_xoffset > text_area_width &&
+ strong_xoffset - (weak_xoffset - text_area_width) >= 0)
+ {
+ entry->scroll_offset += weak_xoffset - text_area_width;
}
- for (i = previous_size; i < entry->text_size; i++)
- entry->text[i] = '\0';
+ gtk_widget_queue_draw (GTK_WIDGET (entry));
}
static void
@@ -1652,165 +1448,78 @@ gtk_entry_insert_text (GtkEditable *editable,
gint new_text_length,
gint *position)
{
- GdkWChar *text;
- gint start_pos;
- gint end_pos;
- gint last_pos;
- gint max_length;
- gint i;
-
- guchar *new_text_nt;
- gint insertion_length;
- GdkWChar *insertion_text;
-
+ gint index;
+ gint n_chars;
GtkEntry *entry;
GtkWidget *widget;
g_return_if_fail (editable != NULL);
g_return_if_fail (GTK_IS_ENTRY (editable));
+ g_return_if_fail (position != NULL);
+ g_return_if_fail (*position >= 0 || *position < GTK_ENTRY (editable)->text_size);
entry = GTK_ENTRY (editable);
widget = GTK_WIDGET (editable);
- if ((entry->text_length == 0) && (entry->use_wchar == FALSE))
- {
- if (!GTK_WIDGET_REALIZED (widget))
- gtk_widget_ensure_style (widget);
- if ((widget->style) && (widget->style->font->type == GDK_FONT_FONTSET))
- entry->use_wchar = TRUE;
- }
-
if (new_text_length < 0)
- {
- new_text_nt = (gchar *)new_text;
- new_text_length = strlen (new_text);
- if (new_text_length <= 0) return;
- }
- else if (new_text_length == 0)
- {
- return;
- }
- else
- {
- /* make a null-terminated copy of new_text */
- new_text_nt = g_new (gchar, new_text_length + 1);
- memcpy (new_text_nt, new_text, new_text_length);
- new_text_nt[new_text_length] = 0;
- }
-
- /* The algorithms here will work as long as, the text size (a
- * multiple of 2), fits into a guint16 but we specify a shorter
- * maximum length so that if the user pastes a very long text, there
- * is not a long hang from the slow X_LOCALE functions. */
-
- if (entry->text_max_length == 0)
- max_length = 2047;
- else
- max_length = MIN (2047, entry->text_max_length);
-
- /* Convert to wide characters */
- insertion_text = g_new (GdkWChar, new_text_length);
- if (entry->use_wchar)
- insertion_length = gdk_mbstowcs (insertion_text, new_text_nt,
- new_text_length);
- else
- for (insertion_length=0; new_text_nt[insertion_length]; insertion_length++)
- insertion_text[insertion_length] = new_text_nt[insertion_length];
- if (new_text_nt != (guchar *)new_text)
- g_free (new_text_nt);
+ new_text_length = strlen (new_text);
- /* Make sure we do not exceed the maximum size of the entry. */
- if (insertion_length + entry->text_length > max_length)
- insertion_length = max_length - entry->text_length;
-
- /* Don't insert anything, if there was nothing to insert. */
- if (insertion_length <= 0)
+ n_chars = unicode_strlen (new_text, new_text_length);
+ if (entry->text_max_length > 0 && n_chars + entry->text_length > entry->text_max_length)
{
- g_free(insertion_text);
- return;
+ gdk_beep ();
+ n_chars = entry->text_max_length - entry->text_length;
}
- /* Make sure we are inserting at integral character position */
- start_pos = *position;
- if (start_pos < 0)
- start_pos = 0;
- else if (start_pos > entry->text_length)
- start_pos = entry->text_length;
-
- end_pos = start_pos + insertion_length;
- last_pos = insertion_length + entry->text_length;
-
- if (editable->selection_start_pos >= *position)
- editable->selection_start_pos += insertion_length;
- if (editable->selection_end_pos >= *position)
- editable->selection_end_pos += insertion_length;
-
- while (last_pos >= entry->text_size)
- gtk_entry_grow_text (entry);
-
- text = entry->text;
- for (i = last_pos - 1; i >= end_pos; i--)
- text[i] = text[i- (end_pos - start_pos)];
- for (i = start_pos; i < end_pos; i++)
- text[i] = insertion_text[i - start_pos];
- g_free (insertion_text);
-
- /* Fix up the the character offsets */
-
- if (GTK_WIDGET_REALIZED (entry))
+ if (new_text_length + entry->n_bytes + 1 > entry->text_size)
{
- gint offset = 0;
-
- for (i = last_pos; i >= end_pos; i--)
- entry->char_offset[i]
- = entry->char_offset[i - insertion_length];
-
- for (i=start_pos; i<end_pos; i++)
+ while (new_text_length + entry->n_bytes + 1 > entry->text_size)
{
- entry->char_offset[i] = entry->char_offset[start_pos] + offset;
- if (editable->visible)
- {
- offset += gdk_char_width_wc (GTK_WIDGET (entry)->style->font,
- entry->text[i]);
- }
+ if (entry->text_size == 0)
+ entry->text_size = MIN_SIZE;
else
{
- offset += gdk_char_width (GTK_WIDGET (entry)->style->font, '*');
+ if (2 * (guint)entry->text_size < MAX_SIZE &&
+ 2 * (guint)entry->text_size > entry->text_size)
+ entry->text_size *= 2;
+ else
+ {
+ entry->text_size = MAX_SIZE;
+ new_text_length = entry->text_size - new_text_length - 1;
+ break;
+ }
}
}
- for (i = end_pos; i <= last_pos; i++)
- entry->char_offset[i] += offset;
+
+ entry->text = g_realloc (entry->text, entry->text_size);
}
- entry->text_length += insertion_length;
- *position = end_pos;
+ index = unicode_offset_to_index (entry->text, *position);
- entry->text_mb_dirty = 1;
- gtk_entry_queue_draw (entry);
-}
+ g_memmove (entry->text + index + new_text_length, entry->text + index, entry->n_bytes - index);
+ memcpy (entry->text + index, new_text, new_text_length);
-/* Recompute the x offsets of all characters in the buffer */
-static void
-gtk_entry_recompute_offsets (GtkEntry *entry)
-{
- gint i;
- gint offset = 0;
+ entry->n_bytes += new_text_length;
+ entry->text_length += n_chars;
- for (i=0; i<entry->text_length; i++)
- {
- entry->char_offset[i] = offset;
- if (GTK_EDITABLE (entry)->visible)
- {
- offset += gdk_char_width_wc (GTK_WIDGET (entry)->style->font,
- entry->text[i]);
- }
- else
- {
- offset += gdk_char_width (GTK_WIDGET (entry)->style->font, '*');
- }
- }
+ /* NUL terminate for safety and convenience */
+ entry->text[entry->n_bytes] = '\0';
+
+ if (editable->current_pos > *position)
+ editable->current_pos += n_chars;
+
+ if (editable->selection_start_pos > *position)
+ editable->selection_start_pos += n_chars;
+
+ if (editable->selection_end_pos > *position)
+ editable->selection_end_pos += n_chars;
+
+ *position += n_chars;
- entry->char_offset[i] = offset;
+ if (entry->layout)
+ pango_layout_set_text (entry->layout, entry->text, entry->n_bytes);
+
+ gtk_entry_queue_draw (entry);
}
static void
@@ -1818,10 +1527,6 @@ gtk_entry_delete_text (GtkEditable *editable,
gint start_pos,
gint end_pos)
{
- GdkWChar *text;
- gint deletion_length;
- gint i;
-
GtkEntry *entry;
g_return_if_fail (editable != NULL);
@@ -1831,41 +1536,33 @@ gtk_entry_delete_text (GtkEditable *editable,
if (end_pos < 0)
end_pos = entry->text_length;
-
- if (editable->selection_start_pos > start_pos)
- editable->selection_start_pos -= MIN(end_pos, editable->selection_start_pos) - start_pos;
- if (editable->selection_end_pos > start_pos)
- editable->selection_end_pos -= MIN(end_pos, editable->selection_end_pos) - start_pos;
if ((start_pos < end_pos) &&
(start_pos >= 0) &&
(end_pos <= entry->text_length))
{
- text = entry->text;
- deletion_length = end_pos - start_pos;
+ gint start_index = unicode_offset_to_index (entry->text, start_pos);
+ gint end_index = unicode_offset_to_index (entry->text, end_pos);
- /* Fix up the character offsets */
- if (GTK_WIDGET_REALIZED (entry))
- {
- gint deletion_width =
- entry->char_offset[end_pos] - entry->char_offset[start_pos];
-
- for (i = 0 ; i <= entry->text_length - end_pos; i++)
- entry->char_offset[start_pos+i] = entry->char_offset[end_pos+i] - deletion_width;
- }
+ g_memmove (entry->text + start_index, entry->text + end_index, entry->n_bytes - end_index);
+ entry->text_length -= (end_pos - start_pos);
+ entry->n_bytes -= (end_index - start_index);
+
+ if (editable->current_pos > start_pos)
+ editable->current_pos -= MIN (editable->current_pos, end_pos) - start_pos;
- for (i = end_pos; i < entry->text_length; i++)
- text[i - deletion_length] = text[i];
+ if (editable->selection_start_pos > start_pos)
+ editable->selection_start_pos -= MIN (editable->selection_start_pos, end_pos) - start_pos;
- for (i = entry->text_length - deletion_length; i < entry->text_length; i++)
- text[i] = '\0';
+ if (editable->selection_end_pos > start_pos)
+ editable->selection_end_pos -= MIN (editable->selection_end_pos, end_pos) - start_pos;
- entry->text_length -= deletion_length;
- editable->current_pos = start_pos;
}
- entry->text_mb_dirty = 1;
gtk_entry_queue_draw (entry);
+
+ if (entry->layout)
+ pango_layout_set_text (entry->layout, entry->text, entry->n_bytes);
}
static void
@@ -1873,7 +1570,9 @@ gtk_entry_update_text (GtkEditable *editable,
gint start_pos,
gint end_pos)
{
- gtk_entry_queue_draw (GTK_ENTRY(editable));
+ GtkEntry *entry = GTK_ENTRY (editable);
+
+ gtk_entry_queue_draw (entry);
}
static gchar *
@@ -1882,6 +1581,7 @@ gtk_entry_get_chars (GtkEditable *editable,
gint end_pos)
{
GtkEntry *entry;
+ gint start_index, end_index;
g_return_val_if_fail (editable != NULL, NULL);
g_return_val_if_fail (GTK_IS_ENTRY (editable), NULL);
@@ -1891,35 +1591,13 @@ gtk_entry_get_chars (GtkEditable *editable,
if (end_pos < 0)
end_pos = entry->text_length;
- start_pos = MIN(entry->text_length, start_pos);
- end_pos = MIN(entry->text_length, end_pos);
+ start_pos = MIN (entry->text_length, start_pos);
+ end_pos = MIN (entry->text_length, end_pos);
- if (start_pos <= end_pos)
- {
- guchar *mbstr;
- if (entry->use_wchar)
- {
- GdkWChar ch;
- if (end_pos >= entry->text_size)
- gtk_entry_grow_text(entry);
- ch = entry->text[end_pos];
- entry->text[end_pos] = 0;
- mbstr = gdk_wcstombs (entry->text + start_pos);
- entry->text[end_pos] = ch;
- return (gchar *)mbstr;
- }
- else
- {
- gint i;
- mbstr = g_new (gchar, end_pos - start_pos + 1);
- for (i=0; i<end_pos-start_pos; i++)
- mbstr[i] = entry->text[start_pos + i];
- mbstr[i] = 0;
- return (gchar *)mbstr;
- }
- }
- else
- return NULL;
+ start_index = unicode_offset_to_index (entry->text, start_pos);
+ end_index = unicode_offset_to_index (entry->text, end_pos);
+
+ return g_strndup (entry->text + start_index, end_index - start_index);
}
static void
@@ -1928,9 +1606,14 @@ gtk_entry_move_cursor (GtkEditable *editable,
gint y)
{
GtkEntry *entry;
+ gint index;
+
entry = GTK_ENTRY (editable);
+ index = unicode_offset_to_index (entry->text, editable->current_pos);
+
/* Horizontal motion */
+
if ((gint)editable->current_pos < -x)
editable->current_pos = 0;
else if (editable->current_pos + x > entry->text_length)
@@ -1941,16 +1624,56 @@ gtk_entry_move_cursor (GtkEditable *editable,
/* Ignore vertical motion */
}
+static void
+gtk_entry_move_cursor_visually (GtkEditable *editable,
+ gint count)
+{
+ GtkEntry *entry;
+ gint index;
+
+ entry = GTK_ENTRY (editable);
+
+ index = unicode_offset_to_index (entry->text, editable->current_pos);
+
+ gtk_entry_ensure_layout (entry);
+
+ while (count != 0)
+ {
+ int new_index, new_trailing;
+
+ if (count > 0)
+ {
+ pango_layout_move_cursor_visually (entry->layout, index, 0, 1, &new_index, &new_trailing);
+ count--;
+ }
+ else
+ {
+ pango_layout_move_cursor_visually (entry->layout, index, 0, -1, &new_index, &new_trailing);
+ count++;
+ }
+
+ if (new_index < 0 || new_index == G_MAXINT)
+ break;
+
+ if (new_trailing)
+ index = unicode_next_utf8 (entry->text + new_index) - entry->text;
+ else
+ index = new_index;
+ }
+
+ editable->current_pos = unicode_index_to_offset (entry->text, index);
+}
+
static void
gtk_move_forward_character (GtkEntry *entry)
{
- gtk_entry_move_cursor (GTK_EDITABLE (entry), 1, 0);
+ gtk_entry_move_cursor_visually (GTK_EDITABLE (entry), 1);
}
static void
gtk_move_backward_character (GtkEntry *entry)
{
- gtk_entry_move_cursor (GTK_EDITABLE (entry), -1, 0);
+ gtk_entry_move_cursor_visually (GTK_EDITABLE (entry), -1);
}
static void
@@ -1967,7 +1690,6 @@ static void
gtk_move_forward_word (GtkEntry *entry)
{
GtkEditable *editable;
- GdkWChar *text;
gint i;
editable = GTK_EDITABLE (entry);
@@ -1981,23 +1703,33 @@ gtk_move_forward_word (GtkEntry *entry)
if (entry->text && (editable->current_pos < entry->text_length))
{
- text = entry->text;
- i = editable->current_pos;
-
- if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i])))
- for (; i < entry->text_length; i++)
- {
- if ((entry->use_wchar) ? gdk_iswalnum (text[i]) : isalnum (text[i]))
- break;
- }
+ PangoLogAttr *log_attrs;
+ gint n_attrs, old_pos;
- for (; i < entry->text_length; i++)
- {
- if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i])))
- break;
- }
+ gtk_entry_ensure_layout (entry);
+ pango_layout_get_log_attrs (entry->layout, &log_attrs, &n_attrs);
+
+ i = old_pos = editable->current_pos;
+
+ /* Advance over white space */
+ while (i < n_attrs && log_attrs[i].is_white)
+ i++;
+
+ /* Find the next word beginning */
+ i++;
+ while (i < n_attrs && !log_attrs[i].is_word_stop)
+ i++;
+
+ editable->current_pos = MAX (entry->text_length, i);
+
+ /* Back up over white space */
+ while (i > 0 && log_attrs[i - 1].is_white)
+ i--;
- editable->current_pos = i;
+ if (i != old_pos)
+ editable->current_pos = i;
+
+ g_free (log_attrs);
}
}
@@ -2005,7 +1737,6 @@ static void
gtk_move_backward_word (GtkEntry *entry)
{
GtkEditable *editable;
- GdkWChar *text;
gint i;
editable = GTK_EDITABLE (entry);
@@ -2019,27 +1750,19 @@ gtk_move_backward_word (GtkEntry *entry)
if (entry->text && editable->current_pos > 0)
{
- text = entry->text;
+ PangoLogAttr *log_attrs;
+ gint n_attrs;
+
+ gtk_entry_ensure_layout (entry);
+ pango_layout_get_log_attrs (entry->layout, &log_attrs, &n_attrs);
+
i = editable->current_pos - 1;
- if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i])))
- for (; i >= 0; i--)
- {
- if ((entry->use_wchar) ? gdk_iswalnum (text[i]) : isalnum (text[i]))
- break;
- }
- for (; i >= 0; i--)
- {
- if ((entry->use_wchar) ? (!gdk_iswalnum (text[i])) : (!isalnum (text[i])))
- {
- i++;
- break;
- }
- }
-
- if (i < 0)
- i = 0;
-
- editable->current_pos = i;
+
+ /* Find the previous word beginning */
+ while (i > 0 && !log_attrs[i].is_word_stop)
+ i--;
+
+ g_free (log_attrs);
}
}
@@ -2207,9 +1930,13 @@ gtk_entry_set_selection (GtkEditable *editable,
gint start,
gint end)
{
+ GtkEntry *entry;
+
g_return_if_fail (editable != NULL);
g_return_if_fail (GTK_IS_ENTRY (editable));
+ entry = GTK_ENTRY (editable);
+
if (end < 0)
end = GTK_ENTRY (editable)->text_length;
@@ -2235,60 +1962,17 @@ gtk_entry_set_max_length (GtkEntry *entry,
g_return_if_fail (GTK_IS_ENTRY (entry));
if (max && entry->text_length > max)
- gtk_editable_delete_text(GTK_EDITABLE(entry), max, -1);
+ gtk_editable_delete_text (GTK_EDITABLE(entry), max, -1);
+
entry->text_max_length = max;
}
-#ifdef USE_XIM
-static void
-gtk_entry_update_ic_attr (GtkWidget *widget)
-{
- GtkEditable *editable = (GtkEditable *) widget;
- GdkICAttributesType mask = 0;
-
- if (editable->ic == NULL)
- return;
-
- gdk_ic_get_attr (editable->ic, editable->ic_attr,
- GDK_IC_PREEDIT_FOREGROUND |
- GDK_IC_PREEDIT_BACKGROUND |
- GDK_IC_PREEDIT_FONTSET);
-
- if (editable->ic_attr->preedit_foreground.pixel !=
- widget->style->fg[GTK_STATE_NORMAL].pixel)
- {
- mask |= GDK_IC_PREEDIT_FOREGROUND;
- editable->ic_attr->preedit_foreground
- = widget->style->fg[GTK_STATE_NORMAL];
- }
- if (editable->ic_attr->preedit_background.pixel !=
- widget->style->base[GTK_STATE_NORMAL].pixel)
- {
- mask |= GDK_IC_PREEDIT_BACKGROUND;
- editable->ic_attr->preedit_background
- = widget->style->base[GTK_STATE_NORMAL];
- }
- if ((gdk_ic_get_style (editable->ic) & GDK_IM_PREEDIT_POSITION) &&
- widget->style->font != NULL &&
- widget->style->font->type == GDK_FONT_FONTSET &&
- !gdk_font_equal (editable->ic_attr->preedit_fontset,
- widget->style->font))
- {
- mask |= GDK_IC_PREEDIT_FONTSET;
- editable->ic_attr->preedit_fontset = widget->style->font;
- }
-
- if (mask)
- gdk_ic_set_attr (editable->ic, editable->ic_attr, mask);
-}
-#endif /* USE_XIM */
static void
gtk_entry_style_set (GtkWidget *widget,
GtkStyle *previous_style)
{
GtkEntry *entry;
- gint scroll_char;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_ENTRY (widget));
@@ -2297,17 +1981,10 @@ gtk_entry_style_set (GtkWidget *widget,
{
entry = GTK_ENTRY (widget);
- scroll_char = gtk_entry_find_position (entry, entry->scroll_offset);
- gtk_entry_recompute_offsets (GTK_ENTRY (widget));
- entry->scroll_offset = entry->char_offset[scroll_char];
entry_adjust_scroll (entry);
gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
gdk_window_set_background (entry->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
-
-#ifdef USE_XIM
- gtk_entry_update_ic_attr (widget);
-#endif
}
}
@@ -2322,12 +1999,21 @@ gtk_entry_state_changed (GtkWidget *widget,
{
gdk_window_set_background (widget->window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
gdk_window_set_background (GTK_ENTRY (widget)->text_area, &widget->style->base[GTK_WIDGET_STATE (widget)]);
-
-#ifdef USE_XIM
- gtk_entry_update_ic_attr (widget);
-#endif
}
if (GTK_WIDGET_DRAWABLE (widget))
gtk_widget_queue_clear(widget);
}
+
+static void
+gtk_entry_commit_cb (GtkIMContext *context,
+ const gchar *str,
+ GtkEntry *entry)
+{
+ GtkEditable *editable = GTK_EDITABLE (entry);
+ gint tmp_pos = editable->current_pos;
+
+ gtk_editable_insert_text (editable, str, strlen (str), &tmp_pos);
+ editable->current_pos = tmp_pos;
+}
+
diff --git a/gtk/gtkentry.h b/gtk/gtkentry.h
index 7ef9f158d..b3f229b49 100644
--- a/gtk/gtkentry.h
+++ b/gtk/gtkentry.h
@@ -30,7 +30,8 @@
#include <gdk/gdk.h>
#include <gtk/gtkeditable.h>
-
+#include <gtk/gtkimcontext.h>
+#include <pango/pango.h>
#ifdef __cplusplus
extern "C" {
@@ -55,27 +56,21 @@ struct _GtkEntry
GdkWindow *text_area;
GdkPixmap *backing_pixmap;
GdkCursor *cursor;
- GdkWChar *text;
+ gchar *text;
+
+ guint16 text_size; /* allocated size, in bytes */
- guint16 text_size; /* allocated size */
- guint16 text_length; /* length in use */
+ guint16 text_length; /* length in use, in chars */
guint16 text_max_length;
- gint scroll_offset;
- guint visible : 1; /* deprecated - see editable->visible */
- guint32 timer;
- guint button;
- /* The x-offset of each character (including the last insertion position)
- * only valid when the widget is realized */
- gint *char_offset;
-
- /* Same as 'text', but in multibyte */
- gchar *text_mb;
- /* If true, 'text' and 'text_mb' are not coherent */
- guint text_mb_dirty : 1;
- /* If true, we use the encoding of wchar_t as the encoding of 'text'.
- * Otherwise we use the encoding of multi-byte characters instead. */
- guint use_wchar : 1;
+ /*< private >*/
+ guint button;
+ guint32 timer;
+ guint16 n_bytes; /* length in use, in bytes */
+ PangoLayout *layout;
+ gint scroll_offset;
+ gint ascent; /* font ascent, in pango units */
+ GtkIMContext *im_context;
};
struct _GtkEntryClass
diff --git a/gtk/gtkenums.h b/gtk/gtkenums.h
index feb22aa00..39d999b3a 100644
--- a/gtk/gtkenums.h
+++ b/gtk/gtkenums.h
@@ -77,6 +77,14 @@ typedef enum
GTK_DIR_RIGHT
} GtkDirectionType;
+/* Reading directions for text */
+typedef enum
+{
+ GTK_TEXT_DIR_NONE,
+ GTK_TEXT_DIR_RTL,
+ GTK_TEXT_DIR_LTR
+} GtkTextDirection;
+
/* justification for label and maybe other widgets (text?) */
typedef enum
{
diff --git a/gtk/gtkfilesel.c b/gtk/gtkfilesel.c
index 1a656256c..3227d6f06 100644
--- a/gtk/gtkfilesel.c
+++ b/gtk/gtkfilesel.c
@@ -518,6 +518,7 @@ gtk_file_selection_init (GtkFileSelection *filesel)
gtk_signal_connect (GTK_OBJECT (filesel->dir_list), "select_row",
(GtkSignalFunc) gtk_file_selection_dir_button,
(gpointer) filesel);
+ gtk_clist_set_column_auto_resize (GTK_CLIST (filesel->dir_list), 0, TRUE);
gtk_clist_column_titles_passive (GTK_CLIST (filesel->dir_list));
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
@@ -537,6 +538,7 @@ gtk_file_selection_init (GtkFileSelection *filesel)
gtk_signal_connect (GTK_OBJECT (filesel->file_list), "select_row",
(GtkSignalFunc) gtk_file_selection_file_button,
(gpointer) filesel);
+ gtk_clist_set_column_auto_resize (GTK_CLIST (filesel->file_list), 0, TRUE);
gtk_clist_column_titles_passive (GTK_CLIST (filesel->file_list));
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
@@ -672,8 +674,11 @@ gtk_file_selection_hide_fileop_buttons (GtkFileSelection *filesel)
g_return_if_fail (filesel != NULL);
g_return_if_fail (GTK_IS_FILE_SELECTION (filesel));
- if (filesel->fileop_ren_file)
- gtk_widget_destroy (filesel->fileop_ren_file);
+ if (filesel->fileop_ren_file)
+ {
+ gtk_widget_destroy (filesel->fileop_ren_file);
+ filesel->fileop_ren_file = NULL;
+ }
if (filesel->fileop_del_file)
{
@@ -1428,7 +1433,6 @@ win32_gtk_add_drives_to_dir_list(GtkWidget *the_dir_list)
gchar buffer[128];
char volumeNameBuf[128];
char formatBuffer[128];
- gint row;
text[1] = NULL;
@@ -1457,7 +1461,7 @@ win32_gtk_add_drives_to_dir_list(GtkWidget *the_dir_list)
#endif
/* Add to the list */
text[0] = formatBuffer;
- row = gtk_clist_append (GTK_CLIST (the_dir_list), text);
+ gtk_clist_append (GTK_CLIST (the_dir_list), text);
}
textPtr += (strlen(textPtr) + 1);
}
@@ -1472,15 +1476,12 @@ gtk_file_selection_populate (GtkFileSelection *fs,
CompletionState *cmpl_state;
PossibleCompletion* poss;
gchar* filename;
- gint row;
gchar* rem_path = rel_path;
gchar* sel_text;
gchar* text[2];
gint did_recurse = FALSE;
gint possible_count = 0;
gint selection_index = -1;
- gint file_list_width;
- gint dir_list_width;
g_return_if_fail (fs != NULL);
g_return_if_fail (GTK_IS_FILE_SELECTION (fs));
@@ -1505,16 +1506,10 @@ gtk_file_selection_populate (GtkFileSelection *fs,
/* Set the dir_list to include ./ and ../ */
text[1] = NULL;
text[0] = "." G_DIR_SEPARATOR_S;
- row = gtk_clist_append (GTK_CLIST (fs->dir_list), text);
+ gtk_clist_append (GTK_CLIST (fs->dir_list), text);
text[0] = ".." G_DIR_SEPARATOR_S;
- row = gtk_clist_append (GTK_CLIST (fs->dir_list), text);
-
- /*reset the max widths of the lists*/
- dir_list_width = gdk_string_width(fs->dir_list->style->font,".." G_DIR_SEPARATOR_S);
- gtk_clist_set_column_width(GTK_CLIST(fs->dir_list),0,dir_list_width);
- file_list_width = 1;
- gtk_clist_set_column_width(GTK_CLIST(fs->file_list),0,file_list_width);
+ gtk_clist_append (GTK_CLIST (fs->dir_list), text);
while (poss)
{
@@ -1530,29 +1525,11 @@ gtk_file_selection_populate (GtkFileSelection *fs,
{
if (strcmp (filename, "." G_DIR_SEPARATOR_S) != 0 &&
strcmp (filename, ".." G_DIR_SEPARATOR_S) != 0)
- {
- int width = gdk_string_width(fs->dir_list->style->font,
- filename);
- row = gtk_clist_append (GTK_CLIST (fs->dir_list), text);
- if(width > dir_list_width)
- {
- dir_list_width = width;
- gtk_clist_set_column_width(GTK_CLIST(fs->dir_list),0,
- width);
- }
- }
+ gtk_clist_append (GTK_CLIST (fs->dir_list), text);
}
else
{
- int width = gdk_string_width(fs->file_list->style->font,
- filename);
- row = gtk_clist_append (GTK_CLIST (fs->file_list), text);
- if(width > file_list_width)
- {
- file_list_width = width;
- gtk_clist_set_column_width(GTK_CLIST(fs->file_list),0,
- width);
- }
+ gtk_clist_append (GTK_CLIST (fs->file_list), text);
}
}
diff --git a/gtk/gtkfontsel.c b/gtk/gtkfontsel.c
index c71206a6e..90c249268 100644
--- a/gtk/gtkfontsel.c
+++ b/gtk/gtkfontsel.c
@@ -1,6 +1,7 @@
/* GTK - The GIMP Toolkit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
+ * Massively updated for Pango by Owen Taylor, May 2000
* GtkFontSelection widget for Gtk+, by Damon Chaplin, May 1998.
* Based on the GnomeFontSelector widget, by Elliot Lee, but major changes.
* The GnomeFontSelector was derived from app/text_tool.c in the GIMP.
@@ -28,95 +29,30 @@
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
-/*
- * Limits:
- *
- * Fontnames - A maximum of MAX_FONTS (32767) fontnames will be retrieved
- * from X Windows with XListFonts(). Any more are ignored.
- * I think this limit may have been set because of a limit in
- * GtkList. It could possibly be increased since we are using
- * GtkClists now, but I'd be surprised if it was reached.
- * Field length - XLFD_MAX_FIELD_LEN is the maximum length that any field of a
- * fontname can be for it to be considered valid. Others are
- * ignored.
- * Properties - Maximum of 65535 choices for each font property - guint16's
- * are used as indices, e.g. in the FontInfo struct.
- * Combinations - Maximum of 65535 combinations of properties for each font
- * family - a guint16 is used in the FontInfo struct.
- * Font size - Minimum font size of 2 pixels/points, since trying to load
- * some fonts with a size of 1 can cause X to hang.
- * (e.g. the Misc Fixed fonts).
- */
-
-/*
- * Possible Improvements:
- *
- * Font Styles - could sort the styles into a reasonable order - regular
- * first, then bold, bold italic etc.
- *
- * I18N - the default preview text is not useful for international
- * fonts. Maybe the first few characters of the font could be
- * displayed instead.
- * - fontsets? should these be handled by the font dialog?
- */
-
-/*
- * Debugging: compile with -DFONTSEL_DEBUG for lots of debugging output.
- */
-
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "gdk/gdk.h"
-/* Protect against the CHARSET struct in Win32 */
-#ifdef GDK_WINDOWING_WIN32
-# define CHARSET CHARSETstruct
-#endif
-#include "gdkx.h"
-#ifdef GDK_WINDOWING_WIN32
-# undef CHARSET
-#endif
#include "gdk/gdkkeysyms.h"
+#include "gtkfontsel.h"
+
#include "gtkbutton.h"
-#include "gtkcheckbutton.h"
#include "gtkclist.h"
#include "gtkentry.h"
-#include "gtkfontsel.h"
#include "gtkframe.h"
#include "gtkhbbox.h"
#include "gtkhbox.h"
#include "gtklabel.h"
-#include "gtknotebook.h"
-#include "gtkradiobutton.h"
+#include "gtkrc.h"
#include "gtksignal.h"
#include "gtktable.h"
#include "gtkvbox.h"
#include "gtkscrolledwindow.h"
#include "gtkintl.h"
-/* The maximum number of fontnames requested with XListFonts(). */
-#define MAX_FONTS 32767
-
-/* This is the largest field length we will accept. If a fontname has a field
- larger than this we will skip it. */
-#define XLFD_MAX_FIELD_LEN 64
-
-/* These are what we use as the standard font sizes, for the size clist.
- Note that when using points we still show these integer point values but
- we work internally in decipoints (and decipoint values can be typed in). */
-static const guint16 font_sizes[] = {
- 8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 26, 28,
- 32, 36, 40, 48, 56, 64, 72
-};
-
-/* Initial font metric & size (Remember point sizes are in decipoints).
- The font size should match one of those in the font_sizes array. */
-#define INITIAL_METRIC GTK_FONT_METRIC_POINTS
-#define INITIAL_FONT_SIZE 140
-
/* This is the default text shown in the preview entry, though the user
can set it. Remember that some fonts only have capital letters. */
#define PREVIEW_TEXT "abcdefghijk ABCDEFGHIJK"
@@ -132,267 +68,61 @@ static const guint16 font_sizes[] = {
#define FONT_STYLE_LIST_WIDTH 170
#define FONT_SIZE_LIST_WIDTH 60
-/* This is the number of fields in an X Logical Font Description font name.
- Note that we count the registry & encoding as 1. */
-#define GTK_XLFD_NUM_FIELDS 13
-
-typedef struct _GtkFontSelInfo GtkFontSelInfo;
-typedef struct _FontInfo FontInfo;
-typedef struct _FontStyle FontStyle;
-
-/* This struct represents one family of fonts (with one foundry), e.g. adobe
- courier or sony fixed. It stores the family name, the index of the foundry
- name, and the index of and number of available styles. */
-struct _FontInfo
-{
- gchar *family;
- guint16 foundry;
- gint style_index;
- guint16 nstyles;
-};
-
-/* This represents one style, as displayed in the Font Style clist. It can
- have a number of available pixel sizes and point sizes. The indexes point
- into the two big fontsel_info->pixel_sizes & fontsel_info->point_sizes
- arrays. The displayed flag is used when displaying styles to remember which
- styles have already been displayed. Note that it is combined with the
- GtkFontType in the flags field. */
-#define GTK_FONT_DISPLAYED (1 << 7)
-struct _FontStyle
-{
- guint16 properties[GTK_NUM_STYLE_PROPERTIES];
- gint pixel_sizes_index;
- guint16 npixel_sizes;
- gint point_sizes_index;
- guint16 npoint_sizes;
- guint8 flags;
-};
-
-struct _GtkFontSelInfo {
-
- /* This is a table with each FontInfo representing one font family+foundry */
- FontInfo *font_info;
- gint nfonts;
-
- /* This stores all the valid combinations of properties for every family.
- Each FontInfo holds an index into its own space in this one big array. */
- FontStyle *font_styles;
- gint nstyles;
-
- /* This stores all the font sizes available for every style.
- Each style holds an index into these arrays. */
- guint16 *pixel_sizes;
- guint16 *point_sizes;
-
- /* These are the arrays of strings of all possible weights, slants,
- set widths, spacings, charsets & foundries, and the amount of space
- allocated for each array. */
- gchar **properties[GTK_NUM_FONT_PROPERTIES];
- guint16 nproperties[GTK_NUM_FONT_PROPERTIES];
- guint16 space_allocated[GTK_NUM_FONT_PROPERTIES];
-};
-
-/* These are the field numbers in the X Logical Font Description fontnames,
- e.g. -adobe-courier-bold-o-normal--25-180-100-100-m-150-iso8859-1 */
-typedef enum
-{
- XLFD_FOUNDRY = 0,
- XLFD_FAMILY = 1,
- XLFD_WEIGHT = 2,
- XLFD_SLANT = 3,
- XLFD_SET_WIDTH = 4,
- XLFD_ADD_STYLE = 5,
- XLFD_PIXELS = 6,
- XLFD_POINTS = 7,
- XLFD_RESOLUTION_X = 8,
- XLFD_RESOLUTION_Y = 9,
- XLFD_SPACING = 10,
- XLFD_AVERAGE_WIDTH = 11,
- XLFD_CHARSET = 12
-} FontField;
-
-/* These are the names of the fields, used on the info & filter page. */
-static const gchar* xlfd_field_names[GTK_XLFD_NUM_FIELDS] = {
- N_("Foundry:"),
- N_("Family:"),
- N_("Weight:"),
- N_("Slant:"),
- N_("Set Width:"),
- N_("Add Style:"),
- N_("Pixel Size:"),
- N_("Point Size:"),
- N_("Resolution X:"),
- N_("Resolution Y:"),
- N_("Spacing:"),
- N_("Average Width:"),
- N_("Charset:"),
-};
-
-/* These are the array indices of the font properties used in several arrays,
- and should match the xlfd_index array below. */
-typedef enum
-{
- WEIGHT = 0,
- SLANT = 1,
- SET_WIDTH = 2,
- SPACING = 3,
- CHARSET = 4,
- FOUNDRY = 5
-} PropertyIndexType;
-
-/* This is used to look up a field in a fontname given one of the above
- property indices. */
-static const FontField xlfd_index[GTK_NUM_FONT_PROPERTIES] = {
- XLFD_WEIGHT,
- XLFD_SLANT,
- XLFD_SET_WIDTH,
- XLFD_SPACING,
- XLFD_CHARSET,
- XLFD_FOUNDRY
-};
-
-/* These are the positions of the properties in the filter table - x, y. */
-static const gint filter_positions[GTK_NUM_FONT_PROPERTIES][2] = {
- { 1, 0 }, { 0, 2 }, { 1, 2 }, { 2, 2 }, { 2, 0 }, { 0, 0 }
-};
-static const gint filter_heights[GTK_NUM_FONT_PROPERTIES] = {
- 100, 70, 70, 40, 100, 100
+/* These are what we use as the standard font sizes, for the size clist.
+ */
+static const guint16 font_sizes[] = {
+ 8, 9, 10, 11, 12, 13, 14, 16, 18, 20, 22, 24, 26, 28,
+ 32, 36, 40, 48, 56, 64, 72
};
-/* This is returned by gtk_font_selection_filter_state to describe if a
- property value is filtered. e.g. if 'bold' has been selected on the filter
- page, then that will return 'FILTERED' and 'black' will be 'NOT_FILTERED'.
- If none of the weight values are selected, they all return 'NOT_SET'. */
-typedef enum
-{
- FILTERED,
- NOT_FILTERED,
- NOT_SET
-} GtkFontPropertyFilterState;
-
-static GtkFontSelInfo *fontsel_info = NULL;
-
-/* The initial size and increment of each of the arrays of property values. */
-#define PROPERTY_ARRAY_INCREMENT 16
-
static void gtk_font_selection_class_init (GtkFontSelectionClass *klass);
-static void gtk_font_selection_init (GtkFontSelection *fontsel);
-static void gtk_font_selection_destroy (GtkObject *object);
-
-/* These are all used for class initialization - loading in the fonts etc. */
-static void gtk_font_selection_get_fonts (void);
-static void gtk_font_selection_insert_font (GSList *fontnames[],
- gint *ntable,
- gchar *fontname);
-static gint gtk_font_selection_insert_field (gchar *fontname,
- gint prop);
+static void gtk_font_selection_init (GtkFontSelection *fontsel);
+static void gtk_font_selection_finalize (GObject *object);
/* These are the callbacks & related functions. */
-static void gtk_font_selection_select_font (GtkWidget *w,
- gint row,
- gint column,
- GdkEventButton *bevent,
- gpointer data);
-static gint gtk_font_selection_on_clist_key_press (GtkWidget *clist,
- GdkEventKey *event,
- GtkFontSelection *fs);
-static gboolean gtk_font_selection_select_next (GtkFontSelection *fs,
- GtkWidget *clist,
- gint step);
-static void gtk_font_selection_show_available_styles
-(GtkFontSelection *fs);
-static void gtk_font_selection_select_best_style (GtkFontSelection *fs,
- gboolean use_first);
-
-static void gtk_font_selection_select_style (GtkWidget *w,
- gint row,
- gint column,
- GdkEventButton *bevent,
- gpointer data);
-static void gtk_font_selection_show_available_sizes
-(GtkFontSelection *fs);
-static gint gtk_font_selection_size_key_press (GtkWidget *w,
- GdkEventKey *event,
- gpointer data);
-static void gtk_font_selection_select_best_size (GtkFontSelection *fs);
-static void gtk_font_selection_select_size (GtkWidget *w,
- gint row,
- gint column,
- GdkEventButton *bevent,
- gpointer data);
-
-static void gtk_font_selection_metric_callback (GtkWidget *w,
- gpointer data);
-static void gtk_font_selection_expose_list (GtkWidget *w,
- GdkEventExpose *event,
- gpointer data);
-static void gtk_font_selection_realize_list (GtkWidget *widget,
- gpointer data);
-
-static void gtk_font_selection_switch_page (GtkWidget *w,
- GtkNotebookPage *page,
- gint page_num,
- gpointer data);
-static void gtk_font_selection_show_font_info (GtkFontSelection *fs);
-
-static void gtk_font_selection_select_filter (GtkWidget *w,
- gint row,
- gint column,
- GdkEventButton *bevent,
- GtkFontSelection *fs);
-static void gtk_font_selection_unselect_filter (GtkWidget *w,
- gint row,
- gint column,
- GdkEventButton *bevent,
- GtkFontSelection *fs);
-static void gtk_font_selection_update_filter (GtkFontSelection *fs);
-static gboolean gtk_font_selection_style_visible (GtkFontSelection *fs,
- FontInfo *font,
- gint style);
-static void gtk_font_selection_reset_filter (GtkWidget *w,
- GtkFontSelection *fs);
-static void gtk_font_selection_on_clear_filter (GtkWidget *w,
- GtkFontSelection *fs);
-static void gtk_font_selection_show_available_fonts
- (GtkFontSelection *fs);
-static void gtk_font_selection_clear_filter (GtkFontSelection *fs);
-static void gtk_font_selection_update_filter_lists(GtkFontSelection *fs);
-static GtkFontPropertyFilterState gtk_font_selection_filter_state
- (GtkFontSelection *fs,
- GtkFontFilterType filter_type,
- gint property,
- gint index);
+static void gtk_font_selection_select_font (GtkWidget *w,
+ gint row,
+ gint column,
+ GdkEventButton *bevent,
+ gpointer data);
+static gint gtk_font_selection_on_clist_key_press (GtkWidget *clist,
+ GdkEventKey *event,
+ GtkFontSelection *fs);
+static gboolean gtk_font_selection_select_next (GtkFontSelection *fs,
+ GtkWidget *clist,
+ gint step);
+
+static void gtk_font_selection_show_available_fonts (GtkFontSelection *fs);
+
+static void gtk_font_selection_show_available_styles (GtkFontSelection *fs);
+static void gtk_font_selection_select_best_style (GtkFontSelection *fs,
+ gboolean use_first);
+static void gtk_font_selection_select_style (GtkWidget *w,
+ gint row,
+ gint column,
+ GdkEventButton *bevent,
+ gpointer data);
+
+static void gtk_font_selection_select_best_size (GtkFontSelection *fs);
+static void gtk_font_selection_show_available_sizes (GtkFontSelection *fs);
+static gint gtk_font_selection_size_key_press (GtkWidget *w,
+ GdkEventKey *event,
+ gpointer data);
+static void gtk_font_selection_select_size (GtkWidget *w,
+ gint row,
+ gint column,
+ GdkEventButton *bevent,
+ gpointer data);
+
+static void gtk_font_selection_expose_list (GtkWidget *w,
+ GdkEventExpose *event,
+ gpointer data);
+
/* Misc. utility functions. */
-static gboolean gtk_font_selection_load_font (GtkFontSelection *fs);
+static void gtk_font_selection_load_font (GtkFontSelection *fs);
static void gtk_font_selection_update_preview (GtkFontSelection *fs);
-static gint gtk_font_selection_find_font (GtkFontSelection *fs,
- gchar *family,
- guint16 foundry);
-static guint16 gtk_font_selection_field_to_index (gchar **table,
- gint ntable,
- gchar *field);
-
-static gchar* gtk_font_selection_expand_slant_code (gchar *slant);
-static gchar* gtk_font_selection_expand_spacing_code(gchar *spacing);
-
-/* Functions for handling X Logical Font Description fontnames. */
-static gboolean gtk_font_selection_is_xlfd_font_name (const gchar *fontname);
-static char* gtk_font_selection_get_xlfd_field (const gchar *fontname,
- FontField field_num,
- gchar *buffer);
-static gchar * gtk_font_selection_create_xlfd (gint size,
- GtkFontMetricType metric,
- gchar *foundry,
- gchar *family,
- gchar *weight,
- gchar *slant,
- gchar *set_width,
- gchar *spacing,
- gchar *charset);
-
-
/* FontSelectionDialog */
static void gtk_font_selection_dialog_class_init (GtkFontSelectionDialogClass *klass);
static void gtk_font_selection_dialog_init (GtkFontSelectionDialog *fontseldiag);
@@ -401,15 +131,8 @@ static gint gtk_font_selection_dialog_on_configure(GtkWidget *widget,
GdkEventConfigure *event,
GtkFontSelectionDialog *fsd);
-#ifdef GDK_WINDOWING_WIN32
-static char *logfont_to_xlfd (const LOGFONT *lfp,
- int size,
- int res,
- int avg_width);
-#endif
-
static GtkWindowClass *font_selection_parent_class = NULL;
-static GtkNotebookClass *font_selection_dialog_parent_class = NULL;
+static GtkVBoxClass *font_selection_dialog_parent_class = NULL;
GtkType
gtk_font_selection_get_type()
@@ -430,7 +153,7 @@ gtk_font_selection_get_type()
(GtkClassInitFunc) NULL,
};
- font_selection_type = gtk_type_unique (GTK_TYPE_NOTEBOOK,
+ font_selection_type = gtk_type_unique (GTK_TYPE_VBOX,
&fontsel_type_info);
}
@@ -440,15 +163,11 @@ gtk_font_selection_get_type()
static void
gtk_font_selection_class_init(GtkFontSelectionClass *klass)
{
- GtkObjectClass *object_class;
-
- object_class = (GtkObjectClass *) klass;
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
- font_selection_parent_class = gtk_type_class (GTK_TYPE_NOTEBOOK);
+ font_selection_parent_class = gtk_type_class (GTK_TYPE_VBOX);
- object_class->destroy = gtk_font_selection_destroy;
-
- gtk_font_selection_get_fonts ();
+ gobject_class->finalize = gtk_font_selection_finalize;
}
static void
@@ -456,70 +175,24 @@ gtk_font_selection_init(GtkFontSelection *fontsel)
{
GtkWidget *scrolled_win;
GtkWidget *text_frame;
- GtkWidget *text_box, *frame;
- GtkWidget *table, *label, *hbox, *hbox2, *clist, *button, *vbox, *alignment;
- gint i, prop, row;
- gchar *titles[] = { NULL, NULL, NULL };
- gchar buffer[128];
- gchar *size;
- gint size_to_match;
- gchar *row_text[3];
- gchar *property, *text;
- gboolean inserted;
-
- /* Number of internationalized titles here must match number
- of NULL initializers above */
- titles[0] = _("Font Property");
- titles[1] = _("Requested Value");
- titles[2] = _("Actual Value");
-
- /* Initialize the GtkFontSelection struct. We do this here in case any
- callbacks are triggered while creating the interface. */
- fontsel->font = NULL;
- fontsel->font_index = -1;
- fontsel->style = -1;
- fontsel->metric = INITIAL_METRIC;
- fontsel->size = INITIAL_FONT_SIZE;
- fontsel->selected_size = INITIAL_FONT_SIZE;
-
- fontsel->filters[GTK_FONT_FILTER_BASE].font_type = GTK_FONT_ALL;
- fontsel->filters[GTK_FONT_FILTER_USER].font_type = GTK_FONT_BITMAP
- | GTK_FONT_SCALABLE;
+ GtkWidget *text_box;
+ GtkWidget *table, *label;
-
- for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
- {
- fontsel->filters[GTK_FONT_FILTER_BASE].property_filters[prop] = NULL;
- fontsel->filters[GTK_FONT_FILTER_BASE].property_nfilters[prop] = 0;
- fontsel->filters[GTK_FONT_FILTER_USER].property_filters[prop] = NULL;
- fontsel->filters[GTK_FONT_FILTER_USER].property_nfilters[prop] = 0;
- }
-
- for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
- fontsel->property_values[prop] = 0;
-
- /* Create the main notebook page. */
- gtk_notebook_set_homogeneous_tabs (GTK_NOTEBOOK (fontsel), TRUE);
- gtk_notebook_set_tab_hborder (GTK_NOTEBOOK (fontsel), 8);
- fontsel->main_vbox = gtk_vbox_new (FALSE, 4);
- gtk_widget_show (fontsel->main_vbox);
- gtk_container_set_border_width (GTK_CONTAINER (fontsel->main_vbox), 6);
- label = gtk_label_new(_("Font"));
- gtk_notebook_append_page (GTK_NOTEBOOK (fontsel),
- fontsel->main_vbox, label);
+ fontsel->context = gtk_widget_create_pango_context (GTK_WIDGET (fontsel));
+ fontsel->font_desc = pango_font_description_from_string ("sans 12");
/* Create the table of font, style & size. */
table = gtk_table_new (3, 3, FALSE);
gtk_widget_show (table);
gtk_table_set_col_spacings(GTK_TABLE(table), 8);
- gtk_box_pack_start (GTK_BOX (fontsel->main_vbox), table, TRUE, TRUE, 0);
+ gtk_box_pack_start (GTK_BOX (fontsel), table, TRUE, TRUE, 0);
- fontsel->font_label = gtk_label_new(_("Font:"));
+ fontsel->font_label = gtk_label_new(_("Family:"));
gtk_misc_set_alignment (GTK_MISC (fontsel->font_label), 0.0, 0.5);
gtk_widget_show (fontsel->font_label);
gtk_table_attach (GTK_TABLE (table), fontsel->font_label, 0, 1, 0, 1,
GTK_FILL, 0, 0, 0);
- label = gtk_label_new(_("Font Style:"));
+ label = gtk_label_new(_("Style:"));
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_widget_show (label);
gtk_table_attach (GTK_TABLE (table), label, 1, 2, 0, 1,
@@ -530,19 +203,21 @@ gtk_font_selection_init(GtkFontSelection *fontsel)
gtk_table_attach (GTK_TABLE (table), label, 2, 3, 0, 1,
GTK_FILL, 0, 0, 0);
- fontsel->font_entry = gtk_entry_new();
+ fontsel->font_entry = gtk_entry_new ();
gtk_entry_set_editable(GTK_ENTRY(fontsel->font_entry), FALSE);
gtk_widget_set_usize (fontsel->font_entry, 20, -1);
gtk_widget_show (fontsel->font_entry);
gtk_table_attach (GTK_TABLE (table), fontsel->font_entry, 0, 1, 1, 2,
GTK_FILL, 0, 0, 0);
- fontsel->font_style_entry = gtk_entry_new();
- gtk_entry_set_editable(GTK_ENTRY(fontsel->font_style_entry), FALSE);
+
+ fontsel->font_style_entry = gtk_entry_new ();
+ gtk_entry_set_editable (GTK_ENTRY(fontsel->font_style_entry), FALSE);
gtk_widget_set_usize (fontsel->font_style_entry, 20, -1);
gtk_widget_show (fontsel->font_style_entry);
gtk_table_attach (GTK_TABLE (table), fontsel->font_style_entry, 1, 2, 1, 2,
GTK_FILL, 0, 0, 0);
- fontsel->size_entry = gtk_entry_new();
+
+ fontsel->size_entry = gtk_entry_new ();
gtk_widget_set_usize (fontsel->size_entry, 20, -1);
gtk_widget_show (fontsel->size_entry);
gtk_table_attach (GTK_TABLE (table), fontsel->size_entry, 2, 3, 1, 2,
@@ -552,8 +227,8 @@ gtk_font_selection_init(GtkFontSelection *fontsel)
fontsel);
/* Create the clists */
- fontsel->font_clist = gtk_clist_new(1);
- gtk_clist_column_titles_hide (GTK_CLIST(fontsel->font_clist));
+ fontsel->font_clist = gtk_clist_new (1);
+ gtk_clist_column_titles_hide (GTK_CLIST (fontsel->font_clist));
gtk_clist_set_column_auto_resize (GTK_CLIST (fontsel->font_clist), 0, TRUE);
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
gtk_widget_set_usize (scrolled_win, FONT_LIST_WIDTH, FONT_LIST_HEIGHT);
@@ -567,8 +242,8 @@ gtk_font_selection_init(GtkFontSelection *fontsel)
GTK_EXPAND | GTK_FILL,
GTK_EXPAND | GTK_FILL, 0, 0);
- fontsel->font_style_clist = gtk_clist_new(1);
- gtk_clist_column_titles_hide (GTK_CLIST(fontsel->font_style_clist));
+ fontsel->font_style_clist = gtk_clist_new (1);
+ gtk_clist_column_titles_hide (GTK_CLIST (fontsel->font_style_clist));
gtk_clist_set_column_auto_resize (GTK_CLIST (fontsel->font_style_clist),
0, TRUE);
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
@@ -582,7 +257,7 @@ gtk_font_selection_init(GtkFontSelection *fontsel)
GTK_EXPAND | GTK_FILL,
GTK_EXPAND | GTK_FILL, 0, 0);
- fontsel->size_clist = gtk_clist_new(1);
+ fontsel->size_clist = gtk_clist_new (1);
gtk_clist_column_titles_hide (GTK_CLIST(fontsel->size_clist));
gtk_clist_set_column_width (GTK_CLIST(fontsel->size_clist), 0, 20);
scrolled_win = gtk_scrolled_window_new (NULL, NULL);
@@ -590,12 +265,11 @@ gtk_font_selection_init(GtkFontSelection *fontsel)
gtk_container_add (GTK_CONTAINER (scrolled_win), fontsel->size_clist);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
- gtk_widget_show(fontsel->size_clist);
- gtk_widget_show(scrolled_win);
+ gtk_widget_show (fontsel->size_clist);
+ gtk_widget_show (scrolled_win);
gtk_table_attach (GTK_TABLE (table), scrolled_win, 2, 3, 2, 3,
GTK_FILL, GTK_FILL, 0, 0);
-
/* Insert the fonts. If there exist fonts with the same family but
different foundries, then the foundry name is appended in brackets. */
gtk_font_selection_show_available_fonts(fontsel);
@@ -611,6 +285,8 @@ gtk_font_selection_init(GtkFontSelection *fontsel)
GTK_SIGNAL_FUNC(gtk_font_selection_expose_list),
fontsel);
+ gtk_font_selection_show_available_styles (fontsel);
+
gtk_signal_connect (GTK_OBJECT (fontsel->font_style_clist), "select_row",
GTK_SIGNAL_FUNC(gtk_font_selection_select_style),
fontsel);
@@ -619,28 +295,8 @@ gtk_font_selection_init(GtkFontSelection *fontsel)
"key_press_event",
GTK_SIGNAL_FUNC(gtk_font_selection_on_clist_key_press),
fontsel);
- gtk_signal_connect_after (GTK_OBJECT (fontsel->font_style_clist),
- "realize",
- GTK_SIGNAL_FUNC(gtk_font_selection_realize_list),
- fontsel);
-
- /* Insert the standard font sizes */
- gtk_clist_freeze (GTK_CLIST(fontsel->size_clist));
- size_to_match = INITIAL_FONT_SIZE;
- if (INITIAL_METRIC == GTK_FONT_METRIC_POINTS)
- size_to_match = size_to_match / 10;
- for (i = 0; i < sizeof(font_sizes) / sizeof(font_sizes[0]); i++)
- {
- sprintf(buffer, "%i", font_sizes[i]);
- size = buffer;
- gtk_clist_append(GTK_CLIST(fontsel->size_clist), &size);
- if (font_sizes[i] == size_to_match)
- {
- gtk_clist_select_row(GTK_CLIST(fontsel->size_clist), i, 0);
- gtk_entry_set_text(GTK_ENTRY(fontsel->size_entry), buffer);
- }
- }
- gtk_clist_thaw (GTK_CLIST(fontsel->size_clist));
+
+ gtk_font_selection_show_available_sizes (fontsel);
gtk_signal_connect (GTK_OBJECT (fontsel->size_clist), "select_row",
GTK_SIGNAL_FUNC(gtk_font_selection_select_size),
@@ -650,57 +306,11 @@ gtk_font_selection_init(GtkFontSelection *fontsel)
GTK_SIGNAL_FUNC(gtk_font_selection_on_clist_key_press),
fontsel);
-
- /* create the Reset Filter & Metric buttons */
- hbox = gtk_hbox_new(FALSE, 8);
- gtk_widget_show (hbox);
- gtk_box_pack_start (GTK_BOX (fontsel->main_vbox), hbox, FALSE, TRUE, 0);
-
- fontsel->filter_button = gtk_button_new_with_label(_("Reset Filter"));
- gtk_misc_set_padding (GTK_MISC (GTK_BIN (fontsel->filter_button)->child),
- 16, 0);
- gtk_widget_show(fontsel->filter_button);
- gtk_box_pack_start (GTK_BOX (hbox), fontsel->filter_button, FALSE, FALSE, 0);
- gtk_widget_set_sensitive (fontsel->filter_button, FALSE);
- gtk_signal_connect (GTK_OBJECT (fontsel->filter_button), "clicked",
- GTK_SIGNAL_FUNC(gtk_font_selection_on_clear_filter),
- fontsel);
-
- hbox2 = gtk_hbox_new(FALSE, 0);
- gtk_widget_show (hbox2);
- gtk_box_pack_end (GTK_BOX (hbox), hbox2, FALSE, FALSE, 0);
-
- label = gtk_label_new(_("Metric:"));
- gtk_widget_show (label);
- gtk_box_pack_start (GTK_BOX (hbox2), label, FALSE, TRUE, 8);
-
- fontsel->points_button = gtk_radio_button_new_with_label(NULL, _("Points"));
- gtk_widget_show (fontsel->points_button);
- gtk_box_pack_start (GTK_BOX (hbox2), fontsel->points_button, FALSE, TRUE, 0);
- if (INITIAL_METRIC == GTK_FONT_METRIC_POINTS)
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fontsel->points_button),
- TRUE);
-
- fontsel->pixels_button = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(fontsel->points_button), _("Pixels"));
- gtk_widget_show (fontsel->pixels_button);
- gtk_box_pack_start (GTK_BOX (hbox2), fontsel->pixels_button, FALSE, TRUE, 0);
- if (INITIAL_METRIC == GTK_FONT_METRIC_PIXELS)
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fontsel->pixels_button),
- TRUE);
-
- gtk_signal_connect(GTK_OBJECT(fontsel->points_button), "toggled",
- (GtkSignalFunc) gtk_font_selection_metric_callback,
- fontsel);
- gtk_signal_connect(GTK_OBJECT(fontsel->pixels_button), "toggled",
- (GtkSignalFunc) gtk_font_selection_metric_callback,
- fontsel);
-
-
/* create the text entry widget */
text_frame = gtk_frame_new (_("Preview:"));
gtk_widget_show (text_frame);
gtk_frame_set_shadow_type(GTK_FRAME(text_frame), GTK_SHADOW_ETCHED_IN);
- gtk_box_pack_start (GTK_BOX (fontsel->main_vbox), text_frame,
+ gtk_box_pack_start (GTK_BOX (fontsel), text_frame,
FALSE, TRUE, 0);
/* This is just used to get a 4-pixel space around the preview entry. */
@@ -714,221 +324,8 @@ gtk_font_selection_init(GtkFontSelection *fontsel)
gtk_widget_set_usize (fontsel->preview_entry, -1, INITIAL_PREVIEW_HEIGHT);
gtk_box_pack_start (GTK_BOX (text_box), fontsel->preview_entry,
TRUE, TRUE, 0);
-
- /* Create the message area */
- fontsel->message_label = gtk_label_new("");
- gtk_widget_show (fontsel->message_label);
- gtk_box_pack_start (GTK_BOX (fontsel->main_vbox), fontsel->message_label,
- FALSE, FALSE, 0);
-
-
- /* Create the font info page */
- fontsel->info_vbox = gtk_vbox_new (FALSE, 4);
- gtk_widget_show (fontsel->info_vbox);
- gtk_container_set_border_width (GTK_CONTAINER (fontsel->info_vbox), 2);
- label = gtk_label_new(_("Font Information"));
- gtk_notebook_append_page (GTK_NOTEBOOK (fontsel),
- fontsel->info_vbox, label);
-
- fontsel->info_clist = gtk_clist_new_with_titles (3, titles);
- gtk_widget_set_usize (fontsel->info_clist, 390, 150);
- gtk_clist_set_column_width(GTK_CLIST(fontsel->info_clist), 0, 130);
- gtk_clist_set_column_width(GTK_CLIST(fontsel->info_clist), 1, 130);
- gtk_clist_set_column_width(GTK_CLIST(fontsel->info_clist), 2, 130);
- gtk_clist_column_titles_passive(GTK_CLIST(fontsel->info_clist));
- scrolled_win = gtk_scrolled_window_new (NULL, NULL);
- gtk_container_add (GTK_CONTAINER (scrolled_win), fontsel->info_clist);
- gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
- GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
- gtk_widget_show(fontsel->info_clist);
- gtk_widget_show(scrolled_win);
- gtk_box_pack_start (GTK_BOX (fontsel->info_vbox), scrolled_win,
- TRUE, TRUE, 0);
-
- /* Insert the property names */
- gtk_clist_freeze (GTK_CLIST(fontsel->info_clist));
- row_text[1] = "";
- row_text[2] = "";
- for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
- {
- row_text[0] = _(xlfd_field_names[i]);
- gtk_clist_append(GTK_CLIST(fontsel->info_clist), row_text);
- gtk_clist_set_shift(GTK_CLIST(fontsel->info_clist), i, 0, 0, 4);
- gtk_clist_set_shift(GTK_CLIST(fontsel->info_clist), i, 1, 0, 4);
- gtk_clist_set_shift(GTK_CLIST(fontsel->info_clist), i, 2, 0, 4);
- }
- gtk_clist_thaw (GTK_CLIST(fontsel->info_clist));
-
- label = gtk_label_new(_("Requested Font Name:"));
- gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
- gtk_widget_show (label);
- gtk_box_pack_start (GTK_BOX (fontsel->info_vbox), label, FALSE, TRUE, 0);
-
- fontsel->requested_font_name = gtk_entry_new();
- gtk_entry_set_editable(GTK_ENTRY(fontsel->requested_font_name), FALSE);
- gtk_widget_show (fontsel->requested_font_name);
- gtk_box_pack_start (GTK_BOX (fontsel->info_vbox),
- fontsel->requested_font_name, FALSE, TRUE, 0);
-
- label = gtk_label_new(_("Actual Font Name:"));
- gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
- gtk_widget_show (label);
- gtk_box_pack_start (GTK_BOX (fontsel->info_vbox), label, FALSE, TRUE, 0);
-
- fontsel->actual_font_name = gtk_entry_new();
- gtk_entry_set_editable(GTK_ENTRY(fontsel->actual_font_name), FALSE);
- gtk_widget_show (fontsel->actual_font_name);
- gtk_box_pack_start (GTK_BOX (fontsel->info_vbox),
- fontsel->actual_font_name, FALSE, TRUE, 0);
-
- sprintf(buffer, _("%i fonts available with a total of %i styles."),
- fontsel_info->nfonts, fontsel_info->nstyles);
- label = gtk_label_new(buffer);
- gtk_widget_show (label);
- gtk_box_pack_start (GTK_BOX (fontsel->info_vbox), label, FALSE, FALSE, 0);
-
- gtk_signal_connect (GTK_OBJECT (fontsel), "switch_page",
- GTK_SIGNAL_FUNC(gtk_font_selection_switch_page),
- fontsel);
-
-
- /* Create the Filter page. */
- fontsel->filter_vbox = gtk_vbox_new (FALSE, 4);
- gtk_widget_show (fontsel->filter_vbox);
- gtk_container_set_border_width (GTK_CONTAINER (fontsel->filter_vbox), 2);
- label = gtk_label_new(_("Filter"));
- gtk_notebook_append_page (GTK_NOTEBOOK (fontsel),
- fontsel->filter_vbox, label);
-
- /* Create the font type checkbuttons. */
- frame = gtk_frame_new (NULL);
- gtk_widget_show (frame);
- gtk_box_pack_start (GTK_BOX (fontsel->filter_vbox), frame, FALSE, TRUE, 0);
-
- hbox = gtk_hbox_new (FALSE, 20);
- gtk_widget_show (hbox);
- gtk_container_add (GTK_CONTAINER (frame), hbox);
- label = gtk_label_new(_("Font Types:"));
- gtk_widget_show (label);
- gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 10);
-
- hbox2 = gtk_hbox_new (TRUE, 0);
- gtk_widget_show (hbox2);
- gtk_box_pack_start (GTK_BOX (hbox), hbox2, FALSE, TRUE, 0);
-
- fontsel->type_bitmaps_button = gtk_check_button_new_with_label (_("Bitmap"));
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_bitmaps_button), TRUE);
- gtk_widget_show (fontsel->type_bitmaps_button);
- gtk_box_pack_start (GTK_BOX (hbox2), fontsel->type_bitmaps_button,
- FALSE, TRUE, 0);
-
- fontsel->type_scalable_button = gtk_check_button_new_with_label (_("Scalable"));
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scalable_button), TRUE);
- gtk_widget_show (fontsel->type_scalable_button);
- gtk_box_pack_start (GTK_BOX (hbox2), fontsel->type_scalable_button,
- FALSE, TRUE, 0);
-
- fontsel->type_scaled_bitmaps_button = gtk_check_button_new_with_label (_("Scaled Bitmap"));
- gtk_widget_show (fontsel->type_scaled_bitmaps_button);
- gtk_box_pack_start (GTK_BOX (hbox2), fontsel->type_scaled_bitmaps_button,
- FALSE, TRUE, 0);
-
- table = gtk_table_new (4, 3, FALSE);
- gtk_table_set_col_spacings(GTK_TABLE(table), 2);
- gtk_widget_show (table);
- gtk_box_pack_start (GTK_BOX (fontsel->filter_vbox), table, TRUE, TRUE, 0);
-
- for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
- {
- gint left = filter_positions[prop][0];
- gint top = filter_positions[prop][1];
-
- label = gtk_label_new(_(xlfd_field_names[xlfd_index[prop]]));
- gtk_misc_set_alignment (GTK_MISC (label), 0.0, 1.0);
- gtk_misc_set_padding (GTK_MISC (label), 0, 2);
- gtk_widget_show(label);
- gtk_table_attach (GTK_TABLE (table), label, left, left + 1,
- top, top + 1, GTK_FILL, GTK_FILL, 0, 0);
-
- clist = gtk_clist_new(1);
- gtk_widget_set_usize (clist, 100, filter_heights[prop]);
- gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_MULTIPLE);
- gtk_clist_column_titles_hide(GTK_CLIST(clist));
- gtk_clist_set_column_auto_resize (GTK_CLIST (clist), 0, TRUE);
- scrolled_win = gtk_scrolled_window_new (NULL, NULL);
- gtk_container_add (GTK_CONTAINER (scrolled_win), clist);
- gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_win),
- GTK_POLICY_AUTOMATIC,
- GTK_POLICY_AUTOMATIC);
- gtk_widget_show(clist);
- gtk_widget_show(scrolled_win);
-
- /* For the bottom-right cell we add the 'Reset Filter' button. */
- if (top == 2 && left == 2)
- {
- vbox = gtk_vbox_new(FALSE, 0);
- gtk_widget_show(vbox);
- gtk_table_attach (GTK_TABLE (table), vbox, left, left + 1,
- top + 1, top + 2, GTK_FILL, GTK_FILL, 0, 0);
-
- gtk_box_pack_start (GTK_BOX (vbox), scrolled_win, TRUE, TRUE, 0);
-
- alignment = gtk_alignment_new(0.5, 0.0, 0.8, 0.0);
- gtk_widget_show(alignment);
- gtk_box_pack_start (GTK_BOX (vbox), alignment, FALSE, TRUE, 4);
-
- button = gtk_button_new_with_label(_("Reset Filter"));
- gtk_widget_show(button);
- gtk_container_add(GTK_CONTAINER(alignment), button);
- gtk_signal_connect (GTK_OBJECT (button), "clicked",
- GTK_SIGNAL_FUNC(gtk_font_selection_reset_filter),
- fontsel);
- }
- else
- gtk_table_attach (GTK_TABLE (table), scrolled_win,
- left, left + 1, top + 1, top + 2,
- GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
-
- gtk_signal_connect (GTK_OBJECT (clist), "select_row",
- GTK_SIGNAL_FUNC(gtk_font_selection_select_filter),
- fontsel);
- gtk_signal_connect (GTK_OBJECT (clist), "unselect_row",
- GTK_SIGNAL_FUNC(gtk_font_selection_unselect_filter),
- fontsel);
-
- /* Insert the property names, expanded, and in sorted order.
- But we make sure that the wildcard '*' is first. */
- gtk_clist_freeze (GTK_CLIST(clist));
- property = N_("*");
- gtk_clist_append(GTK_CLIST(clist), &property);
-
- for (i = 1; i < fontsel_info->nproperties[prop]; i++) {
- property = _(fontsel_info->properties[prop][i]);
- if (prop == SLANT)
- property = gtk_font_selection_expand_slant_code(property);
- else if (prop == SPACING)
- property = gtk_font_selection_expand_spacing_code(property);
-
- inserted = FALSE;
- for (row = 1; row < GTK_CLIST(clist)->rows; row++)
- {
- gtk_clist_get_text(GTK_CLIST(clist), row, 0, &text);
- if (strcmp(property, text) < 0)
- {
- inserted = TRUE;
- gtk_clist_insert(GTK_CLIST(clist), row, &property);
- break;
- }
- }
- if (!inserted)
- row = gtk_clist_append(GTK_CLIST(clist), &property);
- gtk_clist_set_row_data(GTK_CLIST(clist), row, GINT_TO_POINTER (i));
- }
- gtk_clist_select_row(GTK_CLIST(clist), 0, 0);
- gtk_clist_thaw (GTK_CLIST(clist));
- fontsel->filter_clists[prop] = clist;
- }
+ gtk_font_selection_update_preview (fontsel);
}
GtkWidget *
@@ -942,7 +339,7 @@ gtk_font_selection_new()
}
static void
-gtk_font_selection_destroy (GtkObject *object)
+gtk_font_selection_finalize (GObject *object)
{
GtkFontSelection *fontsel;
@@ -950,13 +347,15 @@ gtk_font_selection_destroy (GtkObject *object)
g_return_if_fail (GTK_IS_FONT_SELECTION (object));
fontsel = GTK_FONT_SELECTION (object);
-
- /* All we have to do is unref the font, if we have one. */
+
+ pango_context_unref (fontsel->context);
+ pango_font_description_free (fontsel->font_desc);
+
if (fontsel->font)
gdk_font_unref (fontsel->font);
- if (GTK_OBJECT_CLASS (font_selection_parent_class)->destroy)
- (* GTK_OBJECT_CLASS (font_selection_parent_class)->destroy) (object);
+ if (G_OBJECT_CLASS (font_selection_parent_class)->finalize)
+ (* G_OBJECT_CLASS (font_selection_parent_class)->finalize) (object);
}
@@ -968,7 +367,6 @@ gtk_font_selection_expose_list (GtkWidget *widget,
gpointer data)
{
GtkFontSelection *fontsel;
- FontInfo *font_info;
GList *selection;
gint index;
@@ -977,8 +375,6 @@ gtk_font_selection_expose_list (GtkWidget *widget,
#endif
fontsel = GTK_FONT_SELECTION(data);
- font_info = fontsel_info->font_info;
-
/* Try to scroll the font family clist to the selected item */
selection = GTK_CLIST(fontsel->font_clist)->selection;
if (selection)
@@ -1011,73 +407,69 @@ gtk_font_selection_expose_list (GtkWidget *widget,
}
}
-
-/* This is called when the style clist is realized. We need to set any
- charset rows to insensitive colours. */
+/* This is called when a family is selected in the list. */
static void
-gtk_font_selection_realize_list (GtkWidget *widget,
- gpointer data)
+gtk_font_selection_select_font (GtkWidget *w,
+ gint row,
+ gint column,
+ GdkEventButton *bevent,
+ gpointer data)
{
GtkFontSelection *fontsel;
- gint row;
- GdkColor *inactive_fg, *inactive_bg;
-
-#ifdef FONTSEL_DEBUG
- g_message("In realize_list\n");
-#endif
+ gchar *family_name;
+ gint index;
+
fontsel = GTK_FONT_SELECTION (data);
- /* Set the colours for any charset rows to insensitive. */
- inactive_fg = &fontsel->font_style_clist->style->fg[GTK_STATE_INSENSITIVE];
- inactive_bg = &fontsel->font_style_clist->style->bg[GTK_STATE_INSENSITIVE];
-
- for (row = 0; row < GTK_CLIST (fontsel->font_style_clist)->rows; row++)
+ if (GTK_CLIST (fontsel->font_clist)->selection)
{
- if (GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (fontsel->font_style_clist), row)) == -1)
+ index = GPOINTER_TO_INT (GTK_CLIST (fontsel->font_clist)->selection->data);
+
+ if (gtk_clist_get_text (GTK_CLIST (fontsel->font_clist), index, 0, &family_name) &&
+ strcasecmp (fontsel->font_desc->family_name, family_name) != 0)
{
- gtk_clist_set_foreground (GTK_CLIST (fontsel->font_style_clist),
- row, inactive_fg);
- gtk_clist_set_background (GTK_CLIST (fontsel->font_style_clist),
- row, inactive_bg);
+ g_free (fontsel->font_desc->family_name);
+ fontsel->font_desc->family_name = g_strdup (family_name);
+
+ gtk_font_selection_show_available_styles (fontsel);
+ gtk_font_selection_select_best_style (fontsel, TRUE);
}
}
}
+static int
+cmp_strings (const void *a, const void *b)
+{
+ return strcasecmp (*(const char **)a, *(const char **)b);
+}
-/* This is called when a family is selected in the list. */
static void
-gtk_font_selection_select_font (GtkWidget *w,
- gint row,
- gint column,
- GdkEventButton *bevent,
- gpointer data)
+gtk_font_selection_show_available_fonts (GtkFontSelection *fontsel)
{
- GtkFontSelection *fontsel;
- FontInfo *font_info;
- FontInfo *font;
-
-#ifdef FONTSEL_DEBUG
- g_message("In select_font\n");
-#endif
- fontsel = GTK_FONT_SELECTION(data);
- font_info = fontsel_info->font_info;
-
- if (bevent && !GTK_WIDGET_HAS_FOCUS (w))
- gtk_widget_grab_focus (w);
-
- row = GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (fontsel->font_clist), row));
- font = &font_info[row];
- gtk_entry_set_text(GTK_ENTRY(fontsel->font_entry), font->family);
-
- /* If it is already the current font, just return. */
- if (fontsel->font_index == row)
- return;
+ gchar **families;
+ int n_families, i;
+
+ pango_context_list_families (fontsel->context, &families, &n_families);
+ qsort (families, n_families, sizeof(char *), cmp_strings);
+
+ gtk_clist_freeze (GTK_CLIST (fontsel->font_clist));
+ gtk_clist_clear (GTK_CLIST (fontsel->font_clist));
+
+ for (i=0; i<n_families; i++)
+ {
+ gtk_clist_append (GTK_CLIST (fontsel->font_clist), &families[i]);
+
+ if (!strcasecmp (families[i], fontsel->font_desc->family_name))
+ {
+ gtk_clist_select_row (GTK_CLIST(fontsel->font_clist), i, 0);
+ gtk_entry_set_text(GTK_ENTRY(fontsel->font_entry), families[i]);
+ }
+ }
- fontsel->font_index = row;
- gtk_font_selection_show_available_styles (fontsel);
- gtk_font_selection_select_best_style (fontsel, TRUE);
-}
+ gtk_clist_thaw (GTK_CLIST(fontsel->font_clist));
+ pango_font_map_free_families (families, n_families);
+}
static gint
gtk_font_selection_on_clist_key_press (GtkWidget *clist,
@@ -1132,149 +524,77 @@ gtk_font_selection_select_next (GtkFontSelection *fontsel,
return TRUE;
}
+static int
+compare_font_descriptions (const PangoFontDescription *a, const PangoFontDescription *b)
+{
+ int val = strcasecmp (a->family_name, b->family_name);
+ if (val != 0)
+ return val;
+
+ if (a->weight != b->weight)
+ return a->weight - b->weight;
+
+ if (a->style != b->style)
+ return a->style - b->style;
+
+ if (a->stretch != b->stretch)
+ return a->stretch - b->stretch;
+
+ if (a->variant != b->variant)
+ return a->variant - b->variant;
+
+ return 0;
+}
+
+static int
+font_description_sort_func (const void *a, const void *b)
+{
+ return compare_font_descriptions (*(PangoFontDescription **)a, *(PangoFontDescription **)b);
+}
/* This fills the font style clist with all the possible style combinations
for the current font family. */
static void
gtk_font_selection_show_available_styles (GtkFontSelection *fontsel)
{
- FontInfo *font;
- FontStyle *styles;
- gint style, tmpstyle, row;
- gint weight_index, slant_index, set_width_index, spacing_index;
- gint charset_index;
- gchar *weight, *slant, *set_width, *spacing;
- gchar *charset = NULL;
- gchar *new_item;
- gchar buffer[XLFD_MAX_FIELD_LEN * 6 + 2];
- GdkColor *inactive_fg, *inactive_bg;
- gboolean show_charset;
+ PangoFontDescription **descs;
+ int n_descs, i;
+ gint match_row = 0;
+ gchar *str;
-#ifdef FONTSEL_DEBUG
- g_message("In show_available_styles\n");
-#endif
- font = &fontsel_info->font_info[fontsel->font_index];
- styles = &fontsel_info->font_styles[font->style_index];
-
- gtk_clist_freeze (GTK_CLIST(fontsel->font_style_clist));
- gtk_clist_clear (GTK_CLIST(fontsel->font_style_clist));
-
- /* First we mark all visible styles as not having been displayed yet,
- and check if every style has the same charset. If not then we will
- display the charset in the list before the styles. */
- show_charset = FALSE;
- charset_index = -1;
- for (style = 0; style < font->nstyles; style++)
- {
- if (gtk_font_selection_style_visible(fontsel, font, style))
- {
- styles[style].flags &= ~GTK_FONT_DISPLAYED;
-
- if (charset_index == -1)
- charset_index = styles[style].properties[CHARSET];
- else if (charset_index != styles[style].properties[CHARSET])
- show_charset = TRUE;
- }
- else
- styles[style].flags |= GTK_FONT_DISPLAYED;
- }
-
- /* Step through the undisplayed styles, finding the next charset which
- hasn't been displayed yet. Then display the charset on one line, if
- necessary, and the visible styles indented beneath it. */
- inactive_fg = &fontsel->font_style_clist->style->fg[GTK_STATE_INSENSITIVE];
- inactive_bg = &fontsel->font_style_clist->style->bg[GTK_STATE_INSENSITIVE];
-
- for (style = 0; style < font->nstyles; style++)
+ pango_context_list_fonts (fontsel->context, fontsel->font_desc->family_name, &descs, &n_descs);
+ qsort (descs, n_descs, sizeof(PangoFontDescription *), font_description_sort_func);
+
+ gtk_clist_freeze (GTK_CLIST (fontsel->font_style_clist));
+ gtk_clist_clear (GTK_CLIST (fontsel->font_style_clist));
+
+ for (i=0; i<n_descs; i++)
{
- if (styles[style].flags & GTK_FONT_DISPLAYED)
- continue;
-
- if (show_charset)
- {
- charset_index = styles[style].properties[CHARSET];
- charset = fontsel_info->properties[CHARSET] [charset_index];
- row = gtk_clist_append(GTK_CLIST(fontsel->font_style_clist),
- &charset);
- gtk_clist_set_row_data(GTK_CLIST(fontsel->font_style_clist), row,
- (gpointer) -1);
- if (GTK_WIDGET_REALIZED (fontsel->font_style_clist))
- {
- gtk_clist_set_foreground(GTK_CLIST(fontsel->font_style_clist),
- row, inactive_fg);
- gtk_clist_set_background(GTK_CLIST(fontsel->font_style_clist),
- row, inactive_bg);
- }
- }
+ PangoFontDescription tmp_desc;
+
+ tmp_desc = *descs[i];
+ tmp_desc.family_name = NULL;
+ tmp_desc.size = 0;
+
+ str = pango_font_description_to_string (&tmp_desc);
+ gtk_clist_append (GTK_CLIST (fontsel->font_style_clist), &str);
+
+ if (descs[i]->weight == fontsel->font_desc->weight &&
+ descs[i]->style == fontsel->font_desc->style &&
+ descs[i]->stretch == fontsel->font_desc->stretch &&
+ descs[i]->variant == fontsel->font_desc->variant)
+ match_row = i;
- for (tmpstyle = style; tmpstyle < font->nstyles; tmpstyle++)
- {
- if (styles[tmpstyle].flags & GTK_FONT_DISPLAYED
- || charset_index != styles[tmpstyle].properties[CHARSET])
- continue;
-
- styles[tmpstyle].flags |= GTK_FONT_DISPLAYED;
-
- weight_index = styles[tmpstyle].properties[WEIGHT];
- slant_index = styles[tmpstyle].properties[SLANT];
- set_width_index = styles[tmpstyle].properties[SET_WIDTH];
- spacing_index = styles[tmpstyle].properties[SPACING];
- weight = fontsel_info->properties[WEIGHT] [weight_index];
- slant = fontsel_info->properties[SLANT] [slant_index];
- set_width = fontsel_info->properties[SET_WIDTH][set_width_index];
- spacing = fontsel_info->properties[SPACING] [spacing_index];
-
- /* Convert '(nil)' weights to 'regular', since it looks nicer. */
- if (!g_strcasecmp(weight, N_("(nil)"))) weight = N_("regular");
-
- /* We don't show default values or (nil) in the other properties. */
- if (!g_strcasecmp(slant, "r")) slant = NULL;
- else if (!g_strcasecmp(slant, "(nil)")) slant = NULL;
- else if (!g_strcasecmp(slant, "i")) slant = N_("italic");
- else if (!g_strcasecmp(slant, "o")) slant = N_("oblique");
- else if (!g_strcasecmp(slant, "ri")) slant = N_("reverse italic");
- else if (!g_strcasecmp(slant, "ro")) slant = N_("reverse oblique");
- else if (!g_strcasecmp(slant, "ot")) slant = N_("other");
-
- if (!g_strcasecmp(set_width, "normal")) set_width = NULL;
- else if (!g_strcasecmp(set_width, "(nil)")) set_width = NULL;
-
- if (!g_strcasecmp(spacing, "p")) spacing = NULL;
- else if (!g_strcasecmp(spacing, "(nil)")) spacing = NULL;
- else if (!g_strcasecmp(spacing, "m")) spacing = N_("[M]");
- else if (!g_strcasecmp(spacing, "c")) spacing = N_("[C]");
-
- /* Add the strings together, making sure there is 1 space between
- them */
- strcpy(buffer, _(weight));
- if (slant)
- {
- strcat(buffer, " ");
- strcat(buffer, _(slant));
- }
- if (set_width)
- {
- strcat(buffer, " ");
- strcat(buffer, _(set_width));
- }
- if (spacing)
- {
- strcat(buffer, " ");
- strcat(buffer, _(spacing));
- }
-
- new_item = buffer;
- row = gtk_clist_append(GTK_CLIST(fontsel->font_style_clist),
- &new_item);
- if (show_charset)
- gtk_clist_set_shift(GTK_CLIST(fontsel->font_style_clist), row, 0,
- 0, 4);
- gtk_clist_set_row_data(GTK_CLIST(fontsel->font_style_clist), row,
- GINT_TO_POINTER (tmpstyle));
- }
+ g_free (str);
}
+
+ gtk_clist_select_row (GTK_CLIST (fontsel->font_style_clist), match_row, 0);
+ gtk_clist_get_text (GTK_CLIST (fontsel->font_style_clist), match_row, 0, &str);
+ gtk_entry_set_text (GTK_ENTRY (fontsel->font_style_entry), str);
gtk_clist_thaw (GTK_CLIST(fontsel->font_style_clist));
+
+ pango_font_descriptions_free (descs, n_descs);
}
@@ -1287,53 +607,8 @@ static void
gtk_font_selection_select_best_style(GtkFontSelection *fontsel,
gboolean use_first)
{
- FontInfo *font;
- FontStyle *styles;
- gint row, prop, style, matched;
- gint best_matched = -1, best_style = -1, best_row = -1;
+ gint best_row = 0;
-#ifdef FONTSEL_DEBUG
- g_message("In select_best_style\n");
-#endif
- font = &fontsel_info->font_info[fontsel->font_index];
- styles = &fontsel_info->font_styles[font->style_index];
-
- for (row = 0; row < GTK_CLIST(fontsel->font_style_clist)->rows; row++)
- {
- style = GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (fontsel->font_style_clist), row));
- /* Skip charset rows. */
- if (style == -1)
- continue;
-
- /* If we just want the first style, we've got it. */
- if (use_first)
- {
- best_style = style;
- best_row = row;
- break;
- }
-
- matched = 0;
- for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
- {
- if (fontsel->property_values[prop] == styles[style].properties[prop])
- matched++;
- }
- if (matched > best_matched)
- {
- best_matched = matched;
- best_style = style;
- best_row = row;
- }
- }
- g_return_if_fail (best_style != -1);
- g_return_if_fail (best_row != -1);
-
- fontsel->style = best_style;
-
- for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
- fontsel->property_values[prop] = styles[fontsel->style].properties[prop];
-
gtk_clist_select_row(GTK_CLIST(fontsel->font_style_clist), best_row, 0);
if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_style_clist), best_row)
!= GTK_VISIBILITY_FULL)
@@ -1352,162 +627,65 @@ gtk_font_selection_select_style (GtkWidget *w,
GdkEventButton *bevent,
gpointer data)
{
- GtkFontSelection *fontsel;
- FontInfo *font_info;
- FontInfo *font;
- FontStyle *styles;
- gint style, prop;
+ GtkFontSelection *fontsel = GTK_FONT_SELECTION (data);
+ PangoFontDescription *tmp_desc;
gchar *text;
-
-#ifdef FONTSEL_DEBUG
- g_message("In select_style\n");
-#endif
- fontsel = GTK_FONT_SELECTION(data);
- font_info = fontsel_info->font_info;
- font = &font_info[fontsel->font_index];
- styles = &fontsel_info->font_styles[font->style_index];
+ gint index;
if (bevent && !GTK_WIDGET_HAS_FOCUS (w))
gtk_widget_grab_focus (w);
- /* The style index is stored in the row data, so we just need to copy
- the style values into the fontsel and reload the font. */
- style = GPOINTER_TO_INT (gtk_clist_get_row_data(GTK_CLIST(fontsel->font_style_clist), row));
-
- /* Don't allow selection of charset rows. */
- if (style == -1)
+ if (GTK_CLIST (fontsel->font_style_clist)->selection)
{
- gtk_clist_unselect_row(GTK_CLIST(fontsel->font_style_clist), row, 0);
- return;
+ index = GPOINTER_TO_INT (GTK_CLIST (fontsel->font_style_clist)->selection->data);
+
+ if (gtk_clist_get_text (GTK_CLIST (fontsel->font_style_clist), index, 0, &text))
+ {
+ tmp_desc = pango_font_description_from_string (text);
+
+ fontsel->font_desc->style = tmp_desc->style;
+ fontsel->font_desc->variant = tmp_desc->variant;
+ fontsel->font_desc->weight = tmp_desc->weight;
+ fontsel->font_desc->stretch = tmp_desc->stretch;
+
+ pango_font_description_free (tmp_desc);
+ }
}
-
- gtk_clist_get_text(GTK_CLIST(fontsel->font_style_clist), row, 0, &text);
- gtk_entry_set_text(GTK_ENTRY(fontsel->font_style_entry), text);
-
- for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
- fontsel->property_values[prop] = styles[style].properties[prop];
-
- if (fontsel->style == style)
- return;
-
- fontsel->style = style;
+
gtk_font_selection_show_available_sizes (fontsel);
gtk_font_selection_select_best_size (fontsel);
}
-
-/* This shows all the available sizes in the size clist, according to the
- current metric and the current font & style. */
static void
gtk_font_selection_show_available_sizes (GtkFontSelection *fontsel)
{
- FontInfo *font;
- FontStyle *styles, *style;
- const guint16 *standard_sizes;
- guint16 *bitmapped_sizes;
- gint nstandard_sizes, nbitmapped_sizes;
- gchar buffer[16], *size;
- gfloat bitmap_size_float = 0.;
- guint16 bitmap_size = 0;
- gboolean can_match;
- gint type_filter;
-
-#ifdef FONTSEL_DEBUG
- g_message("In show_available_sizes\n");
-#endif
- font = &fontsel_info->font_info[fontsel->font_index];
- styles = &fontsel_info->font_styles[font->style_index];
- style = &styles[fontsel->style];
-
- standard_sizes = font_sizes;
- nstandard_sizes = sizeof(font_sizes) / sizeof(font_sizes[0]);
- if (fontsel->metric == GTK_FONT_METRIC_POINTS)
- {
- bitmapped_sizes = &fontsel_info->point_sizes[style->point_sizes_index];
- nbitmapped_sizes = style->npoint_sizes;
- }
- else
- {
- bitmapped_sizes = &fontsel_info->pixel_sizes[style->pixel_sizes_index];
- nbitmapped_sizes = style->npixel_sizes;
- }
-
- /* Only show the standard sizes if a scalable font is available. */
- type_filter = fontsel->filters[GTK_FONT_FILTER_BASE].font_type
- & fontsel->filters[GTK_FONT_FILTER_USER].font_type;
+ gint i;
+ gchar buffer[128];
+ gchar *size;
- if (!((style->flags & GTK_FONT_SCALABLE_BITMAP
- && type_filter & GTK_FONT_SCALABLE_BITMAP)
- || (style->flags & GTK_FONT_SCALABLE
- && type_filter & GTK_FONT_SCALABLE)))
- nstandard_sizes = 0;
-
- gtk_clist_freeze (GTK_CLIST(fontsel->size_clist));
- gtk_clist_clear (GTK_CLIST(fontsel->size_clist));
-
- /* Interleave the standard sizes with the bitmapped sizes so we get a list
- of ascending sizes. If the metric is points, we have to convert the
- decipoints to points. */
- while (nstandard_sizes || nbitmapped_sizes)
- {
- can_match = TRUE;
+ /* Insert the standard font sizes */
+ gtk_clist_freeze (GTK_CLIST (fontsel->size_clist));
+ gtk_clist_clear (GTK_CLIST (fontsel->size_clist));
- if (nbitmapped_sizes)
- {
- if (fontsel->metric == GTK_FONT_METRIC_POINTS)
- {
- if (*bitmapped_sizes % 10 != 0)
- can_match = FALSE;
- bitmap_size = *bitmapped_sizes / 10;
- bitmap_size_float = *bitmapped_sizes / 10;
- }
- else
- {
- bitmap_size = *bitmapped_sizes;
- bitmap_size_float = *bitmapped_sizes;
- }
- }
-
- if (can_match && nstandard_sizes && nbitmapped_sizes
- && *standard_sizes == bitmap_size)
- {
- sprintf(buffer, "%i *", *standard_sizes);
- standard_sizes++;
- nstandard_sizes--;
- bitmapped_sizes++;
- nbitmapped_sizes--;
- }
- else if (nstandard_sizes
- && (!nbitmapped_sizes
- || (gfloat)*standard_sizes < bitmap_size_float))
- {
- sprintf(buffer, "%i", *standard_sizes);
- standard_sizes++;
- nstandard_sizes--;
- }
- else
- {
- if (fontsel->metric == GTK_FONT_METRIC_POINTS)
- {
- if (*bitmapped_sizes % 10 == 0)
- sprintf(buffer, "%i *", *bitmapped_sizes / 10);
- else
- sprintf(buffer, "%i.%i *", *bitmapped_sizes / 10,
- *bitmapped_sizes % 10);
- }
- else
- {
- sprintf(buffer, "%i *", *bitmapped_sizes);
- }
- bitmapped_sizes++;
- nbitmapped_sizes--;
- }
+ for (i = 0; i < G_N_ELEMENTS (font_sizes); i++)
+ {
+ sprintf(buffer, "%i", font_sizes[i]);
size = buffer;
- gtk_clist_append(GTK_CLIST(fontsel->size_clist), &size);
+ gtk_clist_append (GTK_CLIST(fontsel->size_clist), &size);
+ if (font_sizes[i] * PANGO_SCALE == fontsel->font_desc->size)
+ gtk_clist_select_row(GTK_CLIST(fontsel->size_clist), i, 0);
}
gtk_clist_thaw (GTK_CLIST(fontsel->size_clist));
+
+ sprintf (buffer, "%i", fontsel->font_desc->size / PANGO_SCALE);
+ gtk_entry_set_text (GTK_ENTRY(fontsel->size_entry), buffer);
}
+static void
+gtk_font_selection_select_best_size (GtkFontSelection *fontsel)
+{
+ gtk_font_selection_load_font (fontsel);
+}
/* If the user hits return in the font size entry, we change to the new font
size. */
@@ -1518,140 +696,25 @@ gtk_font_selection_size_key_press (GtkWidget *w,
{
GtkFontSelection *fontsel;
gint new_size;
- gfloat new_size_float;
gchar *text;
-#ifdef FONTSEL_DEBUG
- g_message("In size_key_press\n");
-#endif
- fontsel = GTK_FONT_SELECTION(data);
+ fontsel = GTK_FONT_SELECTION (data);
if (event->keyval == GDK_Return)
{
text = gtk_entry_get_text (GTK_ENTRY (fontsel->size_entry));
- if (fontsel->metric == GTK_FONT_METRIC_PIXELS)
- {
- new_size = atoi (text);
- if (new_size < 2)
- new_size = 2;
- }
- else
- {
- new_size_float = atof (text) * 10;
- new_size = (gint) new_size_float;
- if (new_size < 20)
- new_size = 20;
- }
-
- /* Remember that this size was set explicitly. */
- fontsel->selected_size = new_size;
-
- /* Check if the font size has changed, and return if it hasn't. */
- if (fontsel->size == new_size)
- return TRUE;
-
- fontsel->size = new_size;
- gtk_font_selection_select_best_size (fontsel);
- return TRUE;
- }
-
- return FALSE;
-}
+ new_size = atoi (text) * PANGO_SCALE;
-
-/* This tries to select the closest size to the current size, though it
- may have to change the size if only unscaled bitmaps are available.
- Note: this will load a font. */
-static void
-gtk_font_selection_select_best_size(GtkFontSelection *fontsel)
-{
- FontInfo *font;
- FontStyle *styles, *style;
- gchar *text;
- gint row, best_row = 0, size, size_fraction, best_size = 0, nmatched;
- gboolean found = FALSE;
- gchar buffer[32];
- GList *selection;
- gint type_filter;
-
-#ifdef FONTSEL_DEBUG
- g_message("In select_best_size\n");
-#endif
- font = &fontsel_info->font_info[fontsel->font_index];
- styles = &fontsel_info->font_styles[font->style_index];
- style = &styles[fontsel->style];
-
- /* Find the closest size available in the size clist. If the exact size is
- in the list set found to TRUE. */
- for (row = 0; row < GTK_CLIST(fontsel->size_clist)->rows; row++)
- {
- gtk_clist_get_text(GTK_CLIST(fontsel->size_clist), row, 0, &text);
- nmatched = sscanf(text, "%i.%i", &size, &size_fraction);
- if (fontsel->metric == GTK_FONT_METRIC_POINTS)
+ if (fontsel->font_desc->size != new_size)
{
- size *= 10;
- if (nmatched == 2)
- size += size_fraction;
- }
-
- if (size == fontsel->selected_size)
- {
- found = TRUE;
- best_size = size;
- best_row = row;
- break;
- }
- else if (best_size == 0
- || abs(size - fontsel->selected_size)
- < (abs(best_size - fontsel->selected_size)))
- {
- best_size = size;
- best_row = row;
+ fontsel->font_desc->size = new_size;
+ gtk_font_selection_load_font (fontsel);
}
}
- /* If we aren't scaling bitmapped fonts and this is a bitmapped font, we
- need to use the closest size found. */
- type_filter = fontsel->filters[GTK_FONT_FILTER_BASE].font_type
- & fontsel->filters[GTK_FONT_FILTER_USER].font_type;
-
- if (!((style->flags & GTK_FONT_SCALABLE_BITMAP
- && type_filter & GTK_FONT_SCALABLE_BITMAP)
- || (style->flags & GTK_FONT_SCALABLE
- && type_filter & GTK_FONT_SCALABLE)))
- found = TRUE;
-
- if (found)
- {
- fontsel->size = best_size;
- gtk_clist_moveto(GTK_CLIST(fontsel->size_clist), best_row, -1, 0.5, 0);
- gtk_clist_select_row(GTK_CLIST(fontsel->size_clist), best_row, 0);
- }
- else
- {
- fontsel->size = fontsel->selected_size;
- selection = GTK_CLIST(fontsel->size_clist)->selection;
- if (selection)
- gtk_clist_unselect_row(GTK_CLIST(fontsel->size_clist),
- GPOINTER_TO_INT (selection->data), 0);
- gtk_clist_moveto(GTK_CLIST(fontsel->size_clist), best_row, -1, 0.5, 0);
-
- /* Show the size in the size entry. */
- if (fontsel->metric == GTK_FONT_METRIC_PIXELS)
- sprintf(buffer, "%i", fontsel->size);
- else
- {
- if (fontsel->size % 10 == 0)
- sprintf(buffer, "%i", fontsel->size / 10);
- else
- sprintf(buffer, "%i.%i", fontsel->size / 10, fontsel->size % 10);
- }
- gtk_entry_set_text (GTK_ENTRY (fontsel->size_entry), buffer);
- }
- gtk_font_selection_load_font (fontsel);
+ return FALSE;
}
-
/* This is called when a size is selected in the list. */
static void
gtk_font_selection_select_size (GtkWidget *w,
@@ -1663,163 +726,33 @@ gtk_font_selection_select_size (GtkWidget *w,
GtkFontSelection *fontsel;
gint new_size;
gchar *text;
- gchar buffer[16];
- gint i;
-#ifdef FONTSEL_DEBUG
- g_message("In select_size\n");
-#endif
- fontsel = GTK_FONT_SELECTION(data);
+ fontsel = GTK_FONT_SELECTION (data);
if (bevent && !GTK_WIDGET_HAS_FOCUS (w))
gtk_widget_grab_focus (w);
- /* Copy the size from the clist to the size entry, but without the bitmapped
- marker ('*'). */
gtk_clist_get_text(GTK_CLIST(fontsel->size_clist), row, 0, &text);
- i = 0;
- while (i < 15 && (text[i] == '.' || (text[i] >= '0' && text[i] <= '9')))
- {
- buffer[i] = text[i];
- i++;
- }
- buffer[i] = '\0';
- gtk_entry_set_text(GTK_ENTRY(fontsel->size_entry), buffer);
-
- /* Check if the font size has changed, and return if it hasn't. */
- new_size = atoi(text);
- if (fontsel->metric == GTK_FONT_METRIC_POINTS)
- new_size *= 10;
-
- if (fontsel->size == new_size)
- return;
+ new_size = atoi (text) * PANGO_SCALE;
- /* If the size was selected by the user we set the selected_size. */
- fontsel->selected_size = new_size;
-
- fontsel->size = new_size;
- gtk_font_selection_load_font (fontsel);
-}
-
-
-/* This is called when the pixels or points radio buttons are pressed. */
-static void
-gtk_font_selection_metric_callback (GtkWidget *w,
- gpointer data)
-{
- GtkFontSelection *fontsel = GTK_FONT_SELECTION(data);
-
-#ifdef FONTSEL_DEBUG
- g_message("In metric_callback\n");
-#endif
- if (GTK_TOGGLE_BUTTON(fontsel->pixels_button)->active)
- {
- if (fontsel->metric == GTK_FONT_METRIC_PIXELS)
- return;
- fontsel->metric = GTK_FONT_METRIC_PIXELS;
- fontsel->size = (fontsel->size + 5) / 10;
- fontsel->selected_size = (fontsel->selected_size + 5) / 10;
- }
- else
- {
- if (fontsel->metric == GTK_FONT_METRIC_POINTS)
- return;
- fontsel->metric = GTK_FONT_METRIC_POINTS;
- fontsel->size *= 10;
- fontsel->selected_size *= 10;
- }
- if (fontsel->font_index != -1)
+ if (fontsel->font_desc->size != new_size)
{
- gtk_font_selection_show_available_sizes (fontsel);
- gtk_font_selection_select_best_size (fontsel);
- }
-}
+ /* If the size was selected by the user we set the selected_size. */
+ fontsel->font_desc->size = new_size;
-
-/* This searches the given property table and returns the index of the given
- string, or 0, which is the wildcard '*' index, if it's not found. */
-static guint16
-gtk_font_selection_field_to_index (gchar **table,
- gint ntable,
- gchar *field)
-{
- gint i;
-
- for (i = 0; i < ntable; i++)
- if (strcmp (field, table[i]) == 0)
- return i;
-
- return 0;
+ gtk_font_selection_load_font (fontsel);
+ }
}
-
-
-/* This attempts to load the current font, and returns TRUE if it succeeds. */
-static gboolean
+static void
gtk_font_selection_load_font (GtkFontSelection *fontsel)
{
- GdkFont *font;
- gchar *fontname, *label_text;
- XFontStruct *xfs;
-
if (fontsel->font)
gdk_font_unref (fontsel->font);
fontsel->font = NULL;
-
- /* If no family has been selected yet, just return FALSE. */
- if (fontsel->font_index == -1)
- return FALSE;
-
- fontname = gtk_font_selection_get_font_name (fontsel);
- if (fontname)
- {
-#ifdef FONTSEL_DEBUG
- g_message("Loading: %s\n", fontname);
-#endif
-#ifndef GDK_WINDOWING_WIN32
- font = gdk_font_load (fontname);
- xfs = font ? GDK_FONT_XFONT (font) : NULL;
- if (xfs && (xfs->min_byte1 != 0 || xfs->max_byte1 != 0))
- {
- gchar *tmp_name;
-
- gdk_font_unref (font);
- tmp_name = g_strconcat (fontname, ",*", NULL);
- font = gdk_fontset_load (tmp_name);
- g_free (tmp_name);
- }
-#else
- /* Load as a fontset so that gtkentry uses wide chars for it */
- font = gdk_fontset_load (fontname);
-#endif
- g_free (fontname);
-
- if (font)
- {
- fontsel->font = font;
- /* Make sure the message label is empty, but don't change it unless
- it's necessary as it results in a resize of the whole window! */
- gtk_label_get(GTK_LABEL(fontsel->message_label), &label_text);
- if (strcmp(label_text, ""))
- gtk_label_set_text(GTK_LABEL(fontsel->message_label), "");
- gtk_font_selection_update_preview (fontsel);
- return TRUE;
- }
- else
- {
- gtk_label_set_text(GTK_LABEL(fontsel->message_label),
- _("The selected font is not available."));
- }
- }
- else
- {
- gtk_label_set_text(GTK_LABEL(fontsel->message_label),
- _("The selected font is not a valid font."));
- }
-
- return FALSE;
-}
+ gtk_font_selection_update_preview (fontsel);
+}
/* This sets the font in the preview entry to the selected font, and tries to
make sure that the preview entry is a reasonable size, i.e. so that the
@@ -1830,1516 +763,34 @@ gtk_font_selection_load_font (GtkFontSelection *fontsel)
static void
gtk_font_selection_update_preview (GtkFontSelection *fontsel)
{
- GtkWidget *preview_entry;
- GtkStyle *style;
- gint text_height, new_height;
+ GtkRcStyle *rc_style;
+ gint new_height;
+ GtkRequisition old_requisition;
+ GtkWidget *preview_entry = fontsel->preview_entry;
gchar *text;
-#ifdef GDK_WINDOWING_X11
- XFontStruct *xfs;
-#endif
-#ifdef FONTSEL_DEBUG
- g_message("In update_preview\n");
-#endif
- style = gtk_style_new ();
- gdk_font_unref (style->font);
- style->font = fontsel->font;
- gdk_font_ref (style->font);
+ gtk_widget_get_child_requisition (preview_entry, &old_requisition);
- preview_entry = fontsel->preview_entry;
- gtk_widget_set_style (preview_entry, style);
- gtk_style_unref(style);
+ rc_style = gtk_rc_style_new ();
+ rc_style->font_desc = pango_font_description_copy (fontsel->font_desc);
+ gtk_widget_modify_style (preview_entry, rc_style);
+ gtk_rc_style_unref (rc_style);
+
+ gtk_widget_size_request (preview_entry, NULL);
- text_height = preview_entry->style->font->ascent
- + preview_entry->style->font->descent;
/* We don't ever want to be over MAX_PREVIEW_HEIGHT pixels high. */
- new_height = text_height + 20;
- if (new_height < INITIAL_PREVIEW_HEIGHT)
- new_height = INITIAL_PREVIEW_HEIGHT;
- if (new_height > MAX_PREVIEW_HEIGHT)
- new_height = MAX_PREVIEW_HEIGHT;
-
- if ((preview_entry->requisition.height < text_height + 10)
- || (preview_entry->requisition.height > text_height + 40))
+ new_height = CLAMP (preview_entry->requisition.height, INITIAL_PREVIEW_HEIGHT, MAX_PREVIEW_HEIGHT);
+
+ if (new_height > old_requisition.height || new_height < old_requisition.height - 30)
gtk_widget_set_usize(preview_entry, -1, new_height);
/* This sets the preview text, if it hasn't been set already. */
- text = gtk_entry_get_text(GTK_ENTRY(fontsel->preview_entry));
+ text = gtk_entry_get_text(GTK_ENTRY(preview_entry));
if (strlen(text) == 0)
- gtk_entry_set_text(GTK_ENTRY(fontsel->preview_entry), PREVIEW_TEXT);
- gtk_entry_set_position(GTK_ENTRY(fontsel->preview_entry), 0);
-
-#ifdef GDK_WINDOWING_X11
- /* If this is a 2-byte font display a message to say it may not be
- displayed properly. */
- xfs = GDK_FONT_XFONT(fontsel->font);
- if (xfs->min_byte1 != 0 || xfs->max_byte1 != 0)
- gtk_label_set_text(GTK_LABEL(fontsel->message_label),
- _("This is a 2-byte font and may not be displayed correctly."));
-#endif
-}
-
-
-static void
-gtk_font_selection_switch_page (GtkWidget *w,
- GtkNotebookPage *page,
- gint page_num,
- gpointer data)
-{
- GtkFontSelection *fontsel = GTK_FONT_SELECTION(data);
-
- /* This function strangely gets called when the window is destroyed,
- so we check here to see if the notebook is visible. */
- if (!GTK_WIDGET_VISIBLE(w))
- return;
-
- if (page_num == 0)
- gtk_font_selection_update_filter(fontsel);
- else if (page_num == 1)
- gtk_font_selection_show_font_info(fontsel);
+ gtk_entry_set_text(GTK_ENTRY(preview_entry), PREVIEW_TEXT);
+ gtk_entry_set_position(GTK_ENTRY(preview_entry), 0);
}
-
-static void
-gtk_font_selection_show_font_info (GtkFontSelection *fontsel)
-{
-#ifdef GDK_WINDOWING_X11
- Atom font_atom, atom;
- Bool status;
-#endif
- char *name;
- gchar *fontname;
- gchar field_buffer[XLFD_MAX_FIELD_LEN];
- gchar *field;
- gint i;
- gboolean shown_actual_fields = FALSE;
-
- fontname = gtk_font_selection_get_font_name(fontsel);
- gtk_entry_set_text(GTK_ENTRY(fontsel->requested_font_name),
- fontname ? fontname : "");
-
- gtk_clist_freeze (GTK_CLIST(fontsel->info_clist));
- for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
- {
- if (fontname)
- field = gtk_font_selection_get_xlfd_field (fontname, i, field_buffer);
- else
- field = NULL;
- if (field)
- {
- if (i == XLFD_SLANT)
- field = gtk_font_selection_expand_slant_code(field);
- else if (i == XLFD_SPACING)
- field = gtk_font_selection_expand_spacing_code(field);
- }
- gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 1,
- field ? field : "");
- }
-#ifdef GDK_WINDOWING_X11
- if (fontsel->font)
- {
- font_atom = gdk_atom_intern ("FONT", FALSE);
-
- if (fontsel->font->type == GDK_FONT_FONTSET)
- {
- XFontStruct **font_structs;
- gint num_fonts;
- gchar **font_names;
-
- num_fonts = XFontsOfFontSet (GDK_FONT_XFONT(fontsel->font),
- &font_structs, &font_names);
- status = XGetFontProperty(font_structs[0], font_atom, &atom);
- }
- else
- {
- status = XGetFontProperty(GDK_FONT_XFONT(fontsel->font), font_atom,
- &atom);
- }
-
- if (status == True)
- {
- name = gdk_atom_name (atom);
- gtk_entry_set_text (GTK_ENTRY (fontsel->actual_font_name), name);
-
- for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
- {
- field = gtk_font_selection_get_xlfd_field (name, i, field_buffer);
- if (i == XLFD_SLANT)
- field = gtk_font_selection_expand_slant_code(field);
- else if (i == XLFD_SPACING)
- field = gtk_font_selection_expand_spacing_code(field);
- gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 2,
- field ? field : "");
- }
- shown_actual_fields = TRUE;
- g_free (name);
- }
- }
-#elif defined (GDK_WINDOWING_WIN32)
- if (fontsel->font)
- {
- LOGFONT logfont;
-
- if (GetObject (GDK_FONT_XFONT (fontsel->font),
- sizeof (LOGFONT), &logfont) > 0)
- {
- name = logfont_to_xlfd (&logfont, logfont.lfHeight, -1, 0);
- gtk_entry_set_text (GTK_ENTRY(fontsel->actual_font_name),name);
- for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
- {
- field = gtk_font_selection_get_xlfd_field (name, i,
- field_buffer);
- if (i == XLFD_SLANT)
- field = gtk_font_selection_expand_slant_code(field);
- else if (i == XLFD_SPACING)
- field = gtk_font_selection_expand_spacing_code(field);
- gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 2,
- field ? field : "");
- }
- shown_actual_fields = TRUE;
- g_free (name);
- }
- }
-
-#endif
- if (!shown_actual_fields)
- {
- gtk_entry_set_text(GTK_ENTRY(fontsel->actual_font_name), "");
- for (i = 0; i < GTK_XLFD_NUM_FIELDS; i++)
- {
- gtk_clist_set_text(GTK_CLIST(fontsel->info_clist), i, 2,
- fontname ? _("(unknown)") : "");
- }
- }
- gtk_clist_thaw (GTK_CLIST(fontsel->info_clist));
- g_free(fontname);
-}
-
-
-static gchar*
-gtk_font_selection_expand_slant_code(gchar *slant)
-{
- if (!g_strcasecmp(slant, "r")) return(_("roman"));
- else if (!g_strcasecmp(slant, "i")) return(_("italic"));
- else if (!g_strcasecmp(slant, "o")) return(_("oblique"));
- else if (!g_strcasecmp(slant, "ri")) return(_("reverse italic"));
- else if (!g_strcasecmp(slant, "ro")) return(_("reverse oblique"));
- else if (!g_strcasecmp(slant, "ot")) return(_("other"));
- return slant;
-}
-
-static gchar*
-gtk_font_selection_expand_spacing_code(gchar *spacing)
-{
- if (!g_strcasecmp(spacing, "p")) return(_("proportional"));
- else if (!g_strcasecmp(spacing, "m")) return(_("monospaced"));
- else if (!g_strcasecmp(spacing, "c")) return(_("char cell"));
- return spacing;
-}
-
-
-/*****************************************************************************
- * These functions all deal with the Filter page and filtering the fonts.
- *****************************************************************************/
-
-/* This is called when an item is selected in one of the filter clists.
- We make sure that the first row of the clist, i.e. the wildcard '*', is
- selected if and only if none of the other items are selected.
- Also doesn't allow selections of values filtered out by base filter.
- We may need to be careful about triggering other signals. */
-static void
-gtk_font_selection_select_filter (GtkWidget *w,
- gint row,
- gint column,
- GdkEventButton *bevent,
- GtkFontSelection *fontsel)
-{
- gint i, prop, index;
-
- if (row == 0)
- {
- for (i = 1; i < GTK_CLIST(w)->rows; i++)
- gtk_clist_unselect_row(GTK_CLIST(w), i, 0);
- }
- else
- {
- /* Find out which property this is. */
- for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
- if (fontsel->filter_clists[prop] == w)
- break;
- index = GPOINTER_TO_INT (gtk_clist_get_row_data(GTK_CLIST(w), row));
- if (gtk_font_selection_filter_state (fontsel, GTK_FONT_FILTER_BASE,
- prop, index) == NOT_FILTERED)
- gtk_clist_unselect_row(GTK_CLIST(w), row, 0);
- else
- gtk_clist_unselect_row(GTK_CLIST(w), 0, 0);
- }
-}
-
-
-/* Here a filter item is being deselected. If there are now no items selected
- we select the first '*' item, unless that it is the item being deselected,
- in which case we select all of the other items. This makes it easy to
- select all items in the list except one or two. */
-static void
-gtk_font_selection_unselect_filter (GtkWidget *w,
- gint row,
- gint column,
- GdkEventButton *bevent,
- GtkFontSelection *fontsel)
-{
- gint i, prop, index;
-
- if (!GTK_CLIST(w)->selection)
- {
- if (row == 0)
- {
- /* Find out which property this is. */
- for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
- if (fontsel->filter_clists[prop] == w)
- break;
-
- for (i = 1; i < GTK_CLIST(w)->rows; i++)
- {
- index = GPOINTER_TO_INT (gtk_clist_get_row_data(GTK_CLIST(w),
- i));
- if (gtk_font_selection_filter_state (fontsel,
- GTK_FONT_FILTER_BASE,
- prop, index)
- != NOT_FILTERED)
- gtk_clist_select_row(GTK_CLIST(w), i, 0);
- }
- }
- else
- {
- gtk_clist_select_row(GTK_CLIST(w), 0, 0);
- }
- }
-}
-
-
-/* This is called when the main notebook page is selected. It checks if the
- filter has changed, an if so it creates the filter settings, and filters the
- fonts shown. If an empty filter (all '*'s) is applied, then filtering is
- turned off. */
-static void
-gtk_font_selection_update_filter (GtkFontSelection *fontsel)
-{
- GtkWidget *clist;
- GList *selection;
- gboolean default_filter = TRUE, filter_changed = FALSE;
- gint prop, nselected, i, row, index;
- GtkFontFilter *filter = &fontsel->filters[GTK_FONT_FILTER_USER];
- gint base_font_type, user_font_type, new_font_type;
-
-#ifdef FONTSEL_DEBUG
- g_message("In update_filter\n");
-#endif
-
- /* Check if the user filter has changed, and also if it is the default
- filter, i.e. bitmap & scalable fonts and all '*'s selected.
- We only look at the bits which are not already filtered out by the base
- filter, since that overrides the user filter. */
- base_font_type = fontsel->filters[GTK_FONT_FILTER_BASE].font_type
- & GTK_FONT_ALL;
- user_font_type = fontsel->filters[GTK_FONT_FILTER_USER].font_type
- & GTK_FONT_ALL;
- new_font_type = GTK_TOGGLE_BUTTON(fontsel->type_bitmaps_button)->active
- ? GTK_FONT_BITMAP : 0;
- new_font_type |= (GTK_TOGGLE_BUTTON(fontsel->type_scalable_button)->active
- ? GTK_FONT_SCALABLE : 0);
- new_font_type |= (GTK_TOGGLE_BUTTON(fontsel->type_scaled_bitmaps_button)->active ? GTK_FONT_SCALABLE_BITMAP : 0);
- new_font_type &= base_font_type;
- new_font_type |= (~base_font_type & user_font_type);
- if (new_font_type != (GTK_FONT_BITMAP | GTK_FONT_SCALABLE))
- default_filter = FALSE;
-
- if (new_font_type != user_font_type)
- filter_changed = TRUE;
- fontsel->filters[GTK_FONT_FILTER_USER].font_type = new_font_type;
-
- for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
- {
- clist = fontsel->filter_clists[prop];
- selection = GTK_CLIST(clist)->selection;
- nselected = g_list_length(selection);
- if (nselected != 1 || GPOINTER_TO_INT (selection->data) != 0)
- {
- default_filter = FALSE;
-
- if (filter->property_nfilters[prop] != nselected)
- filter_changed = TRUE;
- else
- {
- for (i = 0; i < nselected; i++)
- {
- row = GPOINTER_TO_INT (selection->data);
- index = GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (clist), row));
- if (filter->property_filters[prop][i] != index)
- filter_changed = TRUE;
- selection = selection->next;
- }
- }
- }
- else
- {
- if (filter->property_nfilters[prop] != 0)
- filter_changed = TRUE;
- }
- }
-
- /* If the filter hasn't changed we just return. */
- if (!filter_changed)
- return;
-
-#ifdef FONTSEL_DEBUG
- g_message(" update_fonts: filter has changed\n");
-#endif
-
- /* Free the old filter data and create the new arrays. */
- for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
- {
- g_free(filter->property_filters[prop]);
-
- clist = fontsel->filter_clists[prop];
- selection = GTK_CLIST(clist)->selection;
- nselected = g_list_length(selection);
- if (nselected == 1 && GPOINTER_TO_INT (selection->data) == 0)
- {
- filter->property_filters[prop] = NULL;
- filter->property_nfilters[prop] = 0;
- }
- else
- {
- filter->property_filters[prop] = g_new(guint16, nselected);
- filter->property_nfilters[prop] = nselected;
- for (i = 0; i < nselected; i++)
- {
- row = GPOINTER_TO_INT (selection->data);
- index = GPOINTER_TO_INT (gtk_clist_get_row_data (GTK_CLIST (clist), row));
- filter->property_filters[prop][i] = index;
- selection = selection->next;
- }
- }
- }
-
- /* Set the 'Reset Filter' button sensitive if a filter is in effect, and
- also set the label above the font list to show this as well. */
- if (default_filter)
- {
- gtk_widget_set_sensitive(fontsel->filter_button, FALSE);
- gtk_label_set_text(GTK_LABEL(fontsel->font_label), _("Font:"));
- }
- else
- {
- gtk_widget_set_sensitive(fontsel->filter_button, TRUE);
- gtk_label_set_text(GTK_LABEL(fontsel->font_label), _("Font: (Filter Applied)"));
- }
- gtk_font_selection_show_available_fonts(fontsel);
-}
-
-
-/* This shows all the available fonts in the font clist. */
-static void
-gtk_font_selection_show_available_fonts (GtkFontSelection *fontsel)
-{
- FontInfo *font_info, *font;
- GtkFontFilter *filter;
- gint nfonts, i, j, k, row, style, font_row = -1;
- gchar font_buffer[XLFD_MAX_FIELD_LEN * 2 + 4];
- gchar *font_item;
- gboolean matched, matched_style;
-
-#ifdef FONTSEL_DEBUG
- g_message("In show_available_fonts\n");
-#endif
- font_info = fontsel_info->font_info;
- nfonts = fontsel_info->nfonts;
-
- /* Filter the list of fonts. */
- gtk_clist_freeze (GTK_CLIST(fontsel->font_clist));
- gtk_clist_clear (GTK_CLIST(fontsel->font_clist));
- for (i = 0; i < nfonts; i++)
- {
- font = &font_info[i];
-
- /* Check if the foundry passes through all filters. */
- matched = TRUE;
- for (k = 0; k < GTK_NUM_FONT_FILTERS; k++)
- {
- filter = &fontsel->filters[k];
-
- if (filter->property_nfilters[FOUNDRY] != 0)
- {
- matched = FALSE;
- for (j = 0; j < filter->property_nfilters[FOUNDRY]; j++)
- {
- if (font->foundry == filter->property_filters[FOUNDRY][j])
- {
- matched = TRUE;
- break;
- }
- }
- if (!matched)
- break;
- }
- }
-
- if (!matched)
- continue;
-
-
- /* Now check if the other properties are matched in at least one style.*/
- matched_style = FALSE;
- for (style = 0; style < font->nstyles; style++)
- {
- if (gtk_font_selection_style_visible(fontsel, font, style))
- {
- matched_style = TRUE;
- break;
- }
- }
- if (!matched_style)
- continue;
-
- /* Insert the font in the clist. */
- if ((i > 0 && font->family == font_info[i-1].family)
- || (i < nfonts - 1 && font->family == font_info[i+1].family))
- {
- sprintf(font_buffer, "%s (%s)", font->family,
- fontsel_info->properties[FOUNDRY][font->foundry]);
- font_item = font_buffer;
- row = gtk_clist_append(GTK_CLIST(fontsel->font_clist), &font_item);
- }
- else
- {
- row = gtk_clist_append(GTK_CLIST(fontsel->font_clist),
- &font->family);
- }
- gtk_clist_set_row_data(GTK_CLIST(fontsel->font_clist), row,
- GINT_TO_POINTER (i));
- if (fontsel->font_index == i)
- font_row = row;
- }
- gtk_clist_thaw (GTK_CLIST(fontsel->font_clist));
-
- /* If the currently-selected font isn't in the new list, reset the
- selection. */
- if (font_row == -1)
- {
- fontsel->font_index = -1;
- if (fontsel->font)
- gdk_font_unref(fontsel->font);
- fontsel->font = NULL;
- gtk_entry_set_text(GTK_ENTRY(fontsel->font_entry), "");
- gtk_clist_clear (GTK_CLIST(fontsel->font_style_clist));
- gtk_entry_set_text(GTK_ENTRY(fontsel->font_style_entry), "");
- return;
- }
-
- gtk_clist_select_row(GTK_CLIST(fontsel->font_clist), font_row, 0);
- if (gtk_clist_row_is_visible(GTK_CLIST(fontsel->font_clist), font_row)
- != GTK_VISIBILITY_FULL)
- gtk_clist_moveto(GTK_CLIST(fontsel->font_clist), font_row, -1, 0.5, 0);
-
- gtk_font_selection_show_available_styles (fontsel);
- gtk_font_selection_select_best_style (fontsel, FALSE);
-}
-
-
-/* Returns TRUE if the style is not currently filtered out. */
-static gboolean
-gtk_font_selection_style_visible(GtkFontSelection *fontsel,
- FontInfo *font,
- gint style_index)
-{
- FontStyle *styles, *style;
- GtkFontFilter *filter;
- guint16 value;
- gint prop, i, j;
- gboolean matched;
- gint type_filter;
-
- styles = &fontsel_info->font_styles[font->style_index];
- style = &styles[style_index];
-
- /* Check if font_type of style is filtered. */
- type_filter = fontsel->filters[GTK_FONT_FILTER_BASE].font_type
- & fontsel->filters[GTK_FONT_FILTER_USER].font_type;
- if (!(style->flags & type_filter))
- return FALSE;
-
- for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
- {
- value = style->properties[prop];
-
- /* Check each filter. */
- for (i = 0; i < GTK_NUM_FONT_FILTERS; i++)
- {
- filter = &fontsel->filters[i];
-
- if (filter->property_nfilters[prop] != 0)
- {
- matched = FALSE;
- for (j = 0; j < filter->property_nfilters[prop]; j++)
- {
- if (value == filter->property_filters[prop][j])
- {
- matched = TRUE;
- break;
- }
- }
- if (!matched)
- return FALSE;
- }
- }
- }
- return TRUE;
-}
-
-
-/* This resets the font type to bitmap or scalable, and sets all the filter
- clists to the wildcard '*' options. */
-static void
-gtk_font_selection_reset_filter (GtkWidget *w,
- GtkFontSelection *fontsel)
-{
- gint prop, base_font_type;
-
- fontsel->filters[GTK_FONT_FILTER_USER].font_type = GTK_FONT_BITMAP
- | GTK_FONT_SCALABLE;
-
- base_font_type = fontsel->filters[GTK_FONT_FILTER_BASE].font_type;
- if (base_font_type & GTK_FONT_BITMAP)
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_bitmaps_button), TRUE);
- if (base_font_type & GTK_FONT_SCALABLE)
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scalable_button), TRUE);
- if (base_font_type & GTK_FONT_SCALABLE_BITMAP)
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scaled_bitmaps_button), FALSE);
-
- for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
- gtk_clist_select_row(GTK_CLIST(fontsel->filter_clists[prop]), 0, 0);
-}
-
-
-/* This clears the filter, showing all fonts and styles again. */
-static void
-gtk_font_selection_on_clear_filter (GtkWidget *w,
- GtkFontSelection *fontsel)
-{
- gtk_font_selection_clear_filter(fontsel);
-}
-
-
-/* This resets the user filter, showing all fonts and styles which pass the
- base filter again. Note that the font type is set to bitmaps and scalable
- fonts - scaled bitmaps are not shown. */
-static void
-gtk_font_selection_clear_filter (GtkFontSelection *fontsel)
-{
- GtkFontFilter *filter;
- gint prop;
-
-#ifdef FONTSEL_DEBUG
- g_message("In clear_filter\n");
-#endif
- /* Clear the filter data. */
- filter = &fontsel->filters[GTK_FONT_FILTER_USER];
- filter->font_type = GTK_FONT_BITMAP | GTK_FONT_SCALABLE;
- for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
- {
- g_free(filter->property_filters[prop]);
- filter->property_filters[prop] = NULL;
- filter->property_nfilters[prop] = 0;
- }
-
- /* Select all the '*'s on the filter page. */
- gtk_font_selection_reset_filter(NULL, fontsel);
-
- /* Update the main notebook page. */
- gtk_widget_set_sensitive(fontsel->filter_button, FALSE);
- gtk_label_set_text(GTK_LABEL(fontsel->font_label), _("Font:"));
-
- gtk_font_selection_show_available_fonts(fontsel);
-}
-
-
-void
-gtk_font_selection_set_filter (GtkFontSelection *fontsel,
- GtkFontFilterType filter_type,
- GtkFontType font_type,
- gchar **foundries,
- gchar **weights,
- gchar **slants,
- gchar **setwidths,
- gchar **spacings,
- gchar **charsets)
-{
- GtkFontFilter *filter;
- gchar **filter_strings [GTK_NUM_FONT_PROPERTIES];
- gchar *filter_string;
- gchar *property, *property_alt;
- gint prop, nfilters, i, j, num_found;
- gint base_font_type, user_font_type;
- gboolean filter_set;
-
- /* Put them into an array so we can use a simple loop. */
- filter_strings[FOUNDRY] = foundries;
- filter_strings[WEIGHT] = weights;
- filter_strings[SLANT] = slants;
- filter_strings[SET_WIDTH] = setwidths;
- filter_strings[SPACING] = spacings;
- filter_strings[CHARSET] = charsets;
-
- filter = &fontsel->filters[filter_type];
- filter->font_type = font_type;
-
- /* Free the old filter data, and insert the new. */
- for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
- {
- g_free(filter->property_filters[prop]);
- filter->property_filters[prop] = NULL;
- filter->property_nfilters[prop] = 0;
-
- if (filter_strings[prop])
- {
- /* Count how many items in the new array. */
- nfilters = 0;
- while (filter_strings[prop][nfilters])
- nfilters++;
-
- filter->property_filters[prop] = g_new(guint16, nfilters);
- filter->property_nfilters[prop] = 0;
-
- /* Now convert the strings to property indices. */
- num_found = 0;
- for (i = 0; i < nfilters; i++)
- {
- filter_string = filter_strings[prop][i];
- for (j = 0; j < fontsel_info->nproperties[prop]; j++)
- {
- property = _(fontsel_info->properties[prop][j]);
- property_alt = NULL;
- if (prop == SLANT)
- property_alt = gtk_font_selection_expand_slant_code(property);
- else if (prop == SPACING)
- property_alt = gtk_font_selection_expand_spacing_code(property);
- if (!strcmp (filter_string, property)
- || (property_alt && !strcmp (filter_string, property_alt)))
- {
- filter->property_filters[prop][num_found] = j;
- num_found++;
- break;
- }
- }
- }
- filter->property_nfilters[prop] = num_found;
- }
- }
-
- /* Now set the clists on the filter page according to the new filter. */
- gtk_font_selection_update_filter_lists (fontsel);
-
- if (filter_type == GTK_FONT_FILTER_BASE)
- {
- user_font_type = fontsel->filters[GTK_FONT_FILTER_USER].font_type;
- if (font_type & GTK_FONT_BITMAP)
- {
- gtk_widget_set_sensitive (fontsel->type_bitmaps_button, TRUE);
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_bitmaps_button), user_font_type & GTK_FONT_BITMAP);
- }
- else
- {
- gtk_widget_set_sensitive (fontsel->type_bitmaps_button, FALSE);
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_bitmaps_button), FALSE);
- }
-
- if (font_type & GTK_FONT_SCALABLE)
- {
- gtk_widget_set_sensitive (fontsel->type_scalable_button, TRUE);
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scalable_button), user_font_type & GTK_FONT_SCALABLE);
- }
- else
- {
- gtk_widget_set_sensitive (fontsel->type_scalable_button, FALSE);
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scalable_button), FALSE);
- }
-
- if (font_type & GTK_FONT_SCALABLE_BITMAP)
- {
- gtk_widget_set_sensitive (fontsel->type_scaled_bitmaps_button, TRUE);
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scaled_bitmaps_button), user_font_type & GTK_FONT_SCALABLE_BITMAP);
- }
- else
- {
- gtk_widget_set_sensitive (fontsel->type_scaled_bitmaps_button, FALSE);
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scaled_bitmaps_button), FALSE);
- }
- }
- else
- {
- base_font_type = fontsel->filters[GTK_FONT_FILTER_BASE].font_type;
- if (base_font_type & GTK_FONT_BITMAP)
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_bitmaps_button), font_type & GTK_FONT_BITMAP);
-
- if (base_font_type & GTK_FONT_SCALABLE)
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scalable_button), font_type & GTK_FONT_SCALABLE);
-
- if (base_font_type & GTK_FONT_SCALABLE_BITMAP)
- gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (fontsel->type_scaled_bitmaps_button), font_type & GTK_FONT_SCALABLE_BITMAP);
-
- /* If the user filter is not the default, make the 'Reset Filter' button
- sensitive. */
- filter_set = FALSE;
- if (font_type != (GTK_FONT_BITMAP | GTK_FONT_SCALABLE))
- filter_set = TRUE;
- for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
- {
- if (filter->property_nfilters[prop] != 0)
- filter_set = TRUE;
- }
- if (filter_set)
- gtk_widget_set_sensitive (fontsel->filter_button, TRUE);
- }
-
- gtk_font_selection_show_available_fonts (fontsel);
-}
-
-
-/* This sets the colour of each property in the filter clists according to
- the base filter. i.e. Filtered properties are shown as insensitive. */
-static void
-gtk_font_selection_update_filter_lists (GtkFontSelection *fontsel)
-{
- GtkWidget *clist;
- GdkColor *inactive_fg, *inactive_bg, *fg, *bg;
- gint prop, row, index;
-
- /* We have to make sure the clist is realized to use the colours. */
- for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
- {
- clist = fontsel->filter_clists[prop];
- gtk_widget_realize (clist);
- inactive_fg = &clist->style->fg[GTK_STATE_INSENSITIVE];
- inactive_bg = &clist->style->bg[GTK_STATE_INSENSITIVE];
- for (row = 1; row < GTK_CLIST(clist)->rows; row++)
- {
- index = GPOINTER_TO_INT (gtk_clist_get_row_data(GTK_CLIST(clist),
- row));
- /* Set the colour according to the base filter. */
- if (gtk_font_selection_filter_state (fontsel, GTK_FONT_FILTER_BASE,
- prop, index) == NOT_FILTERED)
- {
- fg = inactive_fg;
- bg = inactive_bg;
- }
- else
- {
- fg = NULL;
- bg = NULL;
- }
- gtk_clist_set_foreground(GTK_CLIST(clist), row, fg);
- gtk_clist_set_background(GTK_CLIST(clist), row, bg);
-
- /* Set the selection state according to the user filter. */
- if (gtk_font_selection_filter_state (fontsel, GTK_FONT_FILTER_USER,
- prop, index) == FILTERED
- && fg == NULL)
- gtk_clist_select_row (GTK_CLIST (clist), row, 0);
- else
- gtk_clist_unselect_row (GTK_CLIST (clist), row, 0);
- }
- }
-}
-
-
-/* Returns whether a property value is in the filter or not, or if the
- property has no filter set. */
-static GtkFontPropertyFilterState
-gtk_font_selection_filter_state (GtkFontSelection *fontsel,
- GtkFontFilterType filter_type,
- gint property,
- gint index)
-{
- GtkFontFilter *filter;
- gint i;
-
- filter = &fontsel->filters[filter_type];
- if (filter->property_nfilters[property] == 0)
- return NOT_SET;
-
- for (i = 0; i < filter->property_nfilters[property]; i++)
- {
- if (filter->property_filters[property][i] == index)
- return FILTERED;
- }
- return NOT_FILTERED;
-}
-
-
-#ifdef GDK_WINDOWING_WIN32
-
-static gint num_fonts;
-static gint font_names_size;
-static gchar **xfontnames;
-static HDC hdc;
-
-static char *
-logfont_to_xlfd (const LOGFONT *lfp,
- int size,
- int res,
- int avg_width)
-{
- const gchar *weight;
- const gchar *registry, *encoding;
- int point_size;
- static int logpixelsy = 0;
- gchar facename[LF_FACESIZE*3];
- gchar *p;
- const gchar *q;
-
- if (logpixelsy == 0)
- {
- HDC hdc = GetDC (NULL);
- logpixelsy = GetDeviceCaps (hdc, LOGPIXELSY);
- ReleaseDC (NULL, hdc);
- }
-
- /* Don't use _() here, only N_(), the actual translation is done elsewhere */
- if (lfp->lfWeight >= FW_HEAVY)
- weight = N_("heavy");
- else if (lfp->lfWeight >= FW_EXTRABOLD)
- weight = N_("extrabold");
- else if (lfp->lfWeight >= FW_BOLD)
- weight = N_("bold");
-#ifdef FW_DEMIBOLD
- else if (lfp->lfWeight >= FW_DEMIBOLD)
- weight = N_("demibold");
-#endif
- else if (lfp->lfWeight >= FW_MEDIUM)
- weight = N_("medium");
- else if (lfp->lfWeight >= FW_NORMAL)
- weight = N_("normal");
- else if (lfp->lfWeight >= FW_LIGHT)
- weight = N_("light");
- else if (lfp->lfWeight >= FW_EXTRALIGHT)
- weight = N_("extralight");
- else if (lfp->lfWeight >= FW_THIN)
- weight = N_("thin");
- else
- weight = N_("regular");
-
- if (lfp->lfCharSet == ANSI_CHARSET)
- {
- registry = "iso8859";
- encoding = "1";
- }
- else
- {
- registry = "windows";
- if (lfp->lfCharSet == DEFAULT_CHARSET)
- encoding = "default";
- else if (lfp->lfCharSet == SYMBOL_CHARSET)
- encoding = "symbol";
- else if (lfp->lfCharSet == SHIFTJIS_CHARSET)
- encoding = "shiftjis";
- else if (lfp->lfCharSet == GB2312_CHARSET)
- encoding = "gb2312";
- else if (lfp->lfCharSet == HANGEUL_CHARSET)
- encoding = "hangeul";
- else if (lfp->lfCharSet == CHINESEBIG5_CHARSET)
- encoding = "chinesebig5";
- else if (lfp->lfCharSet == OEM_CHARSET)
- encoding = "oem";
-#ifdef JOHAB_CHARSET
- else if (lfp->lfCharSet == JOHAB_CHARSET)
- encoding = "johab";
-#endif
- else if (lfp->lfCharSet == HEBREW_CHARSET)
- encoding = "hebrew";
- else if (lfp->lfCharSet == ARABIC_CHARSET)
- encoding = "arabic";
- else if (lfp->lfCharSet == GREEK_CHARSET)
- encoding = "greek";
- else if (lfp->lfCharSet == TURKISH_CHARSET)
- encoding = "turkish";
- else if (lfp->lfCharSet == THAI_CHARSET)
- encoding = "thai";
- else if (lfp->lfCharSet == EASTEUROPE_CHARSET)
- encoding = "easteurope";
- else if (lfp->lfCharSet == RUSSIAN_CHARSET)
- encoding = "russian";
- else if (lfp->lfCharSet == MAC_CHARSET)
- encoding = "mac";
- else if (lfp->lfCharSet == BALTIC_CHARSET)
- encoding = "baltic";
- else
- encoding = "unknown";
- }
-
- point_size = (int) (((double) size/logpixelsy) * 720.);
-
- if (res == -1)
- res = logpixelsy;
-
- /* Replace illegal characters with hex escapes. */
- p = facename;
- q = lfp->lfFaceName;
- while (*q)
- {
- if (*q == '-' || *q == '*' || *q == '?' || *q == '%')
- p += sprintf (p, "%%%.02x", *q);
- else
- *p++ = *q;
- q++;
- }
- *p = '\0';
-
- return g_strdup_printf
- ("-%s-%s-%s-%s-%s-%s-%d-%d-%d-%d-%s-%d-%s-%s",
- "unknown",
- facename,
- weight,
- (lfp->lfItalic ?
- ((lfp->lfPitchAndFamily & 0xF0) == FF_ROMAN
- || (lfp->lfPitchAndFamily & 0xF0) == FF_SCRIPT ?
- "i" : "o") : "r"),
- "normal",
- "",
- size,
- point_size,
- res,
- res,
- ((lfp->lfPitchAndFamily & 0x03) == FIXED_PITCH ? "m" : "p"),
- avg_width,
- registry, encoding);
-}
-
-int CALLBACK
-InnerEnumFontFamExProc (const LOGFONT *lfp,
- const TEXTMETRIC *metrics,
- DWORD fontType,
- LPARAM lParam)
-{
- int size;
-
- if (fontType == TRUETYPE_FONTTYPE)
- {
- size = 0;
- }
- else
- {
- size = lfp->lfHeight;
- }
-
- num_fonts++;
- if (num_fonts == font_names_size)
- {
- font_names_size *= 2;
- xfontnames = g_realloc (xfontnames, font_names_size * sizeof (gchar *));
- }
- xfontnames[num_fonts-1] =
- logfont_to_xlfd (lfp, size, 0, 0);
- return 1;
-}
-
-int CALLBACK
-EnumFontFamExProc (const LOGFONT *lfp,
- const TEXTMETRIC *metrics,
- DWORD fontType,
- LPARAM lParam)
-{
- if (fontType == TRUETYPE_FONTTYPE)
- {
- LOGFONT lf = *lfp;
- lf.lfPitchAndFamily = 0;
- EnumFontFamiliesEx (hdc, &lf, InnerEnumFontFamExProc, 0, 0);
- }
- else
- InnerEnumFontFamExProc (lfp, metrics, fontType, lParam);
- return 1;
-}
-
-#endif
-
-/*****************************************************************************
- * These functions all deal with creating the main class arrays containing
- * the data about all available fonts.
- *****************************************************************************/
-static void
-gtk_font_selection_get_fonts (void)
-{
-#ifdef GDK_WINDOWING_X11
- gchar **xfontnames;
- gint num_fonts;
-#elif defined (GDK_WINDOWING_WIN32)
- LOGFONT logfont;
-#else
- gint num_fonts = 0;
- gchar **xfontnames = NULL;
-#endif
- GSList **fontnames;
- gchar *fontname;
- GSList * temp_list;
- gint i, prop, style, size;
- gint npixel_sizes = 0, npoint_sizes = 0;
- FontInfo *font;
- FontStyle *current_style, *prev_style, *tmp_style;
- gboolean matched_style, found_size;
- gint pixels, points, res_x, res_y;
- gchar field_buffer[XLFD_MAX_FIELD_LEN];
- gchar *field;
- guint8 flags;
- guint16 *pixel_sizes, *point_sizes, *tmp_sizes;
-
- fontsel_info = g_new (GtkFontSelInfo, 1);
-
-#ifdef GDK_WINDOWING_X11
- /* Get a maximum of MAX_FONTS fontnames from the X server.
- Use "-*" as the pattern rather than "-*-*-*-*-*-*-*-*-*-*-*-*-*-*" since
- the latter may result in fonts being returned which don't actually exist.
- xlsfonts also uses "*" so I think it's OK. "-*" gets rid of aliases. */
- xfontnames = XListFonts (GDK_DISPLAY(), "-*", MAX_FONTS, &num_fonts);
- /* Output a warning if we actually get MAX_FONTS fonts. */
- if (num_fonts == MAX_FONTS)
- g_warning(_("MAX_FONTS exceeded. Some fonts may be missing."));
-
-#elif defined (GDK_WINDOWING_WIN32)
- num_fonts = 0;
- hdc = GetDC (NULL);
- font_names_size = 100;
- xfontnames = g_new (gchar *, font_names_size);
- logfont.lfCharSet = DEFAULT_CHARSET;
- logfont.lfFaceName[0] = '\0';
- logfont.lfPitchAndFamily = 0;
- EnumFontFamiliesEx (hdc, &logfont, EnumFontFamExProc, 0, 0);
- ReleaseDC (NULL, hdc);
-#endif
-
- /* The maximum size of all these tables is the number of font names
- returned. We realloc them later when we know exactly how many
- unique entries there are. */
- fontsel_info->font_info = g_new (FontInfo, num_fonts);
- fontsel_info->font_styles = g_new (FontStyle, num_fonts);
- fontsel_info->pixel_sizes = g_new (guint16, num_fonts);
- fontsel_info->point_sizes = g_new (guint16, num_fonts);
-
- fontnames = g_new (GSList*, num_fonts);
-
- /* Create the initial arrays for the property value strings, though they
- may be realloc'ed later. Put the wildcard '*' in the first elements. */
- for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
- {
- fontsel_info->properties[prop] = g_new(gchar*, PROPERTY_ARRAY_INCREMENT);
- fontsel_info->space_allocated[prop] = PROPERTY_ARRAY_INCREMENT;
- fontsel_info->nproperties[prop] = 1;
- fontsel_info->properties[prop][0] = "*";
- }
-
-
- /* Insert the font families into the main table, sorted by family and
- foundry (fonts with different foundries are placed in seaparate FontInfos.
- All fontnames in each family + foundry are placed into the fontnames
- array of lists. */
- fontsel_info->nfonts = 0;
- for (i = 0; i < num_fonts; i++)
- {
- if (gtk_font_selection_is_xlfd_font_name (xfontnames[i]))
- gtk_font_selection_insert_font (fontnames, &fontsel_info->nfonts, xfontnames[i]);
- }
-
-
- /* Since many font names will be in the same FontInfo not all of the
- allocated FontInfo table will be used, so we will now reallocate it
- with the real size. */
- fontsel_info->font_info = g_realloc(fontsel_info->font_info,
- sizeof(FontInfo) * fontsel_info->nfonts);
-
-
- /* Now we work out which choices of weight/slant etc. are valid for each
- font. */
- fontsel_info->nstyles = 0;
- current_style = fontsel_info->font_styles;
- for (i = 0; i < fontsel_info->nfonts; i++)
- {
- font = &fontsel_info->font_info[i];
-
- /* Use the next free position in the styles array. */
- font->style_index = fontsel_info->nstyles;
-
- /* Now step through each of the fontnames with this family, and create
- a style for each fontname. Each style contains the index into the
- weights/slants etc. arrays, and a number of pixel/point sizes. */
- style = 0;
- temp_list = fontnames[i];
- while (temp_list)
- {
- fontname = temp_list->data;
- temp_list = temp_list->next;
-
- for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
- {
- current_style->properties[prop]
- = gtk_font_selection_insert_field (fontname, prop);
- }
- current_style->pixel_sizes_index = npixel_sizes;
- current_style->npixel_sizes = 0;
- current_style->point_sizes_index = npoint_sizes;
- current_style->npoint_sizes = 0;
- current_style->flags = 0;
-
-
- field = gtk_font_selection_get_xlfd_field (fontname, XLFD_PIXELS,
- field_buffer);
- pixels = atoi(field);
-
- field = gtk_font_selection_get_xlfd_field (fontname, XLFD_POINTS,
- field_buffer);
- points = atoi(field);
-
- field = gtk_font_selection_get_xlfd_field (fontname,
- XLFD_RESOLUTION_X,
- field_buffer);
- res_x = atoi(field);
-
- field = gtk_font_selection_get_xlfd_field (fontname,
- XLFD_RESOLUTION_Y,
- field_buffer);
- res_y = atoi(field);
-
- if (pixels == 0 && points == 0)
- {
- if (res_x == 0 && res_y == 0)
- flags = GTK_FONT_SCALABLE;
- else
- flags = GTK_FONT_SCALABLE_BITMAP;
- }
- else
- flags = GTK_FONT_BITMAP;
-
- /* Now we check to make sure that the style is unique. If it isn't
- we forget it. */
- prev_style = fontsel_info->font_styles + font->style_index;
- matched_style = FALSE;
- while (prev_style < current_style)
- {
- matched_style = TRUE;
- for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
- {
- if (prev_style->properties[prop]
- != current_style->properties[prop])
- {
- matched_style = FALSE;
- break;
- }
- }
- if (matched_style)
- break;
- prev_style++;
- }
-
- /* If we matched an existing style, we need to add the pixels &
- point sizes to the style. If not, we insert the pixel & point
- sizes into our new style. Note that we don't add sizes for
- scalable fonts. */
- if (matched_style)
- {
- prev_style->flags |= flags;
- if (flags == GTK_FONT_BITMAP)
- {
- pixel_sizes = fontsel_info->pixel_sizes
- + prev_style->pixel_sizes_index;
- found_size = FALSE;
- for (size = 0; size < prev_style->npixel_sizes; size++)
- {
- if (pixels == *pixel_sizes)
- {
- found_size = TRUE;
- break;
- }
- else if (pixels < *pixel_sizes)
- break;
- pixel_sizes++;
- }
- /* We need to move all the following pixel sizes up, and also
- update the indexes of any following styles. */
- if (!found_size)
- {
- for (tmp_sizes = fontsel_info->pixel_sizes + npixel_sizes;
- tmp_sizes > pixel_sizes; tmp_sizes--)
- *tmp_sizes = *(tmp_sizes - 1);
-
- *pixel_sizes = pixels;
- npixel_sizes++;
- prev_style->npixel_sizes++;
-
- tmp_style = prev_style + 1;
- while (tmp_style < current_style)
- {
- tmp_style->pixel_sizes_index++;
- tmp_style++;
- }
- }
-
- point_sizes = fontsel_info->point_sizes
- + prev_style->point_sizes_index;
- found_size = FALSE;
- for (size = 0; size < prev_style->npoint_sizes; size++)
- {
- if (points == *point_sizes)
- {
- found_size = TRUE;
- break;
- }
- else if (points < *point_sizes)
- break;
- point_sizes++;
- }
- /* We need to move all the following point sizes up, and also
- update the indexes of any following styles. */
- if (!found_size)
- {
- for (tmp_sizes = fontsel_info->point_sizes + npoint_sizes;
- tmp_sizes > point_sizes; tmp_sizes--)
- *tmp_sizes = *(tmp_sizes - 1);
-
- *point_sizes = points;
- npoint_sizes++;
- prev_style->npoint_sizes++;
-
- tmp_style = prev_style + 1;
- while (tmp_style < current_style)
- {
- tmp_style->point_sizes_index++;
- tmp_style++;
- }
- }
- }
- }
- else
- {
- current_style->flags = flags;
- if (flags == GTK_FONT_BITMAP)
- {
- fontsel_info->pixel_sizes[npixel_sizes++] = pixels;
- current_style->npixel_sizes = 1;
- fontsel_info->point_sizes[npoint_sizes++] = points;
- current_style->npoint_sizes = 1;
- }
- style++;
- fontsel_info->nstyles++;
- current_style++;
- }
- }
- g_slist_free(fontnames[i]);
-
- /* Set nstyles to the real value, minus duplicated fontnames.
- Note that we aren't using all the allocated memory if fontnames are
- duplicated. */
- font->nstyles = style;
- }
-
- /* Since some repeated styles may be skipped we won't have used all the
- allocated space, so we will now reallocate it with the real size. */
- fontsel_info->font_styles = g_realloc(fontsel_info->font_styles,
- sizeof(FontStyle) * fontsel_info->nstyles);
- fontsel_info->pixel_sizes = g_realloc(fontsel_info->pixel_sizes,
- sizeof(guint16) * npixel_sizes);
- fontsel_info->point_sizes = g_realloc(fontsel_info->point_sizes,
- sizeof(guint16) * npoint_sizes);
- g_free(fontnames);
-
-#ifdef GDK_WINDOWING_X11
- XFreeFontNames (xfontnames);
-#elif defined (GDK_WINDOWING_WIN32)
- for (i = 0; i < num_fonts; i++)
- g_free (xfontnames[i]);
- g_free (xfontnames);
-#endif
-
- /* Debugging Output */
- /* This outputs all FontInfos. */
-#ifdef FONTSEL_DEBUG
- g_message("\n\n Font Family Weight Slant Set Width Spacing Charset\n\n");
- for (i = 0; i < fontsel_info->nfonts; i++)
- {
- FontInfo *font = &fontsel_info->font_info[i];
- FontStyle *styles = fontsel_info->font_styles + font->style_index;
- for (style = 0; style < font->nstyles; style++)
- {
- g_message("%5i %-16.16s ", i, font->family);
- for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
- g_message("%-9.9s ",
- fontsel_info->properties[prop][styles->properties[prop]]);
- g_message("\n ");
-
- if (styles->flags & GTK_FONT_BITMAP)
- g_message("Bitmapped font ");
- if (styles->flags & GTK_FONT_SCALABLE)
- g_message("Scalable font ");
- if (styles->flags & GTK_FONT_SCALABLE_BITMAP)
- g_message("Scalable-Bitmapped font ");
- g_message("\n");
-
- if (styles->npixel_sizes)
- {
- g_message(" Pixel sizes: ");
- tmp_sizes = fontsel_info->pixel_sizes + styles->pixel_sizes_index;
- for (size = 0; size < styles->npixel_sizes; size++)
- g_message("%i ", *tmp_sizes++);
- g_message("\n");
- }
-
- if (styles->npoint_sizes)
- {
- g_message(" Point sizes: ");
- tmp_sizes = fontsel_info->point_sizes + styles->point_sizes_index;
- for (size = 0; size < styles->npoint_sizes; size++)
- g_message("%i ", *tmp_sizes++);
- g_message("\n");
- }
-
- g_message("\n");
- styles++;
- }
- }
- /* This outputs all available properties. */
- for (prop = 0; prop < GTK_NUM_FONT_PROPERTIES; prop++)
- {
- g_message("Property: %s\n", xlfd_field_names[xlfd_index[prop]]);
- for (i = 0; i < fontsel_info->nproperties[prop]; i++)
- g_message(" %s\n", fontsel_info->properties[prop][i]);
- }
-#endif
-}
-
-/* This inserts the given fontname into the FontInfo table.
- If a FontInfo already exists with the same family and foundry, then the
- fontname is added to the FontInfos list of fontnames, else a new FontInfo
- is created and inserted in alphabetical order in the table. */
-static void
-gtk_font_selection_insert_font (GSList *fontnames[],
- gint *ntable,
- gchar *fontname)
-{
- FontInfo *table;
- FontInfo temp_info;
- GSList *temp_fontname;
- gchar *family;
- gboolean family_exists = FALSE;
- gint foundry;
- gint lower, upper;
- gint middle, cmp;
- gchar family_buffer[XLFD_MAX_FIELD_LEN];
-
- table = fontsel_info->font_info;
-
- /* insert a fontname into a table */
- family = gtk_font_selection_get_xlfd_field (fontname, XLFD_FAMILY,
- family_buffer);
- if (!family)
- return;
-
- foundry = gtk_font_selection_insert_field (fontname, FOUNDRY);
-
- lower = 0;
- if (*ntable > 0)
- {
- /* Do a binary search to determine if we have already encountered
- * a font with this family & foundry. */
- upper = *ntable;
- while (lower < upper)
- {
- middle = (lower + upper) >> 1;
-
- cmp = strcmp (family, table[middle].family);
- /* If the family matches we sort by the foundry. */
- if (cmp == 0)
- {
- family_exists = TRUE;
- family = table[middle].family;
- cmp = strcmp(fontsel_info->properties[FOUNDRY][foundry],
- fontsel_info->properties[FOUNDRY][table[middle].foundry]);
- }
-
- if (cmp == 0)
- {
- fontnames[middle] = g_slist_prepend (fontnames[middle],
- fontname);
- return;
- }
- else if (cmp < 0)
- upper = middle;
- else
- lower = middle+1;
- }
- }
-
- /* Add another entry to the table for this new font family */
- temp_info.family = family_exists ? family : g_strdup(family);
- temp_info.foundry = foundry;
- temp_fontname = g_slist_prepend (NULL, fontname);
-
- (*ntable)++;
-
- /* Quickly insert the entry into the table in sorted order
- * using a modification of insertion sort and the knowledge
- * that the entries proper position in the table was determined
- * above in the binary search and is contained in the "lower"
- * variable. */
- if (*ntable > 1)
- {
- upper = *ntable - 1;
- while (lower != upper)
- {
- table[upper] = table[upper-1];
- fontnames[upper] = fontnames[upper-1];
- upper--;
- }
- }
- table[lower] = temp_info;
- fontnames[lower] = temp_fontname;
-}
-
-
-/* This checks that the specified field of the given fontname is in the
- appropriate properties array. If not it is added. Thus eventually we get
- arrays of all possible weights/slants etc. It returns the array index. */
-static gint
-gtk_font_selection_insert_field (gchar *fontname,
- gint prop)
-{
- gchar field_buffer[XLFD_MAX_FIELD_LEN];
- gchar *field;
- guint16 index;
-
- field = gtk_font_selection_get_xlfd_field (fontname, xlfd_index[prop],
- field_buffer);
- if (!field)
- return 0;
-
- /* If the field is already in the array just return its index. */
- for (index = 0; index < fontsel_info->nproperties[prop]; index++)
- if (!strcmp(field, fontsel_info->properties[prop][index]))
- return index;
-
- /* Make sure we have enough space to add the field. */
- if (fontsel_info->nproperties[prop] == fontsel_info->space_allocated[prop])
- {
- fontsel_info->space_allocated[prop] += PROPERTY_ARRAY_INCREMENT;
- fontsel_info->properties[prop] = g_realloc(fontsel_info->properties[prop],
- sizeof(gchar*)
- * fontsel_info->space_allocated[prop]);
- }
-
- /* Add the new field. */
- index = fontsel_info->nproperties[prop];
- fontsel_info->properties[prop][index] = g_strdup(field);
- fontsel_info->nproperties[prop]++;
- return index;
-}
-
-
/*****************************************************************************
* These functions are the main public interface for getting/setting the font.
*****************************************************************************/
@@ -3347,7 +798,9 @@ gtk_font_selection_insert_field (gchar *fontname,
GdkFont*
gtk_font_selection_get_font (GtkFontSelection *fontsel)
{
- g_return_val_if_fail (fontsel != NULL, NULL);
+ if (!fontsel->font)
+ fontsel->font = gdk_font_from_description (fontsel->font_desc);
+
return fontsel->font;
}
@@ -3355,42 +808,7 @@ gtk_font_selection_get_font (GtkFontSelection *fontsel)
gchar *
gtk_font_selection_get_font_name (GtkFontSelection *fontsel)
{
- FontInfo *font;
- gchar *family_str, *foundry_str;
- gchar *property_str[GTK_NUM_STYLE_PROPERTIES];
- gint prop;
-
- g_return_val_if_fail (fontsel != NULL, NULL);
- g_return_val_if_fail (GTK_IS_FONT_SELECTION (fontsel), NULL);
-
- /* If no family has been selected return NULL. */
- if (fontsel->font_index == -1)
- return NULL;
-
- font = &fontsel_info->font_info[fontsel->font_index];
- family_str = font->family;
- foundry_str = fontsel_info->properties[FOUNDRY][font->foundry];
-
- /* some fonts have a (nil) foundry */
- if (strcmp (foundry_str, "(nil)") == 0)
- foundry_str = "";
-
- for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
- {
- property_str[prop] = fontsel_info->properties[prop][fontsel->property_values[prop]];
- if (strcmp (property_str[prop], "(nil)") == 0)
- property_str[prop] = "";
- }
-
- return gtk_font_selection_create_xlfd (fontsel->size,
- fontsel->metric,
- foundry_str,
- family_str,
- property_str[WEIGHT],
- property_str[SLANT],
- property_str[SET_WIDTH],
- property_str[SPACING],
- property_str[CHARSET]);
+ return pango_font_description_to_string (fontsel->font_desc);
}
@@ -3403,136 +821,41 @@ gboolean
gtk_font_selection_set_font_name (GtkFontSelection *fontsel,
const gchar *fontname)
{
- gchar *family, *field;
- gint index, prop, size;
- guint16 foundry, value;
- gchar family_buffer[XLFD_MAX_FIELD_LEN];
- gchar field_buffer[XLFD_MAX_FIELD_LEN];
- gchar buffer[16];
-
+ PangoFontDescription *new_desc;
+ PangoFontDescription **descs;
+ int n_descs, i;
+ gboolean found = FALSE;
+
g_return_val_if_fail (fontsel != NULL, FALSE);
g_return_val_if_fail (GTK_IS_FONT_SELECTION (fontsel), FALSE);
- g_return_val_if_fail (fontname != NULL, FALSE);
-
- /* Check it is a valid fontname. */
- if (!gtk_font_selection_is_xlfd_font_name(fontname))
- return FALSE;
-
- family = gtk_font_selection_get_xlfd_field (fontname, XLFD_FAMILY,
- family_buffer);
- if (!family)
- return FALSE;
-
- field = gtk_font_selection_get_xlfd_field (fontname, XLFD_FOUNDRY,
- field_buffer);
- foundry = gtk_font_selection_field_to_index (fontsel_info->properties[FOUNDRY],
- fontsel_info->nproperties[FOUNDRY],
- field);
-
- index = gtk_font_selection_find_font(fontsel, family, foundry);
- if (index == -1)
- return FALSE;
-
- /* Convert the property fields into indices and set them. */
- for (prop = 0; prop < GTK_NUM_STYLE_PROPERTIES; prop++)
- {
- field = gtk_font_selection_get_xlfd_field (fontname, xlfd_index[prop],
- field_buffer);
- value = gtk_font_selection_field_to_index (fontsel_info->properties[prop],
- fontsel_info->nproperties[prop],
- field);
- fontsel->property_values[prop] = value;
- }
- field = gtk_font_selection_get_xlfd_field (fontname, XLFD_POINTS,
- field_buffer);
- size = atoi(field);
- if (size > 0)
- {
- if (size < 20)
- size = 20;
- fontsel->size = fontsel->selected_size = size;
- fontsel->metric = GTK_FONT_METRIC_POINTS;
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fontsel->points_button),
- TRUE);
- if (size % 10 == 0)
- sprintf (buffer, "%i", size / 10);
- else
- sprintf (buffer, "%i.%i", size / 10, size % 10);
- }
- else
- {
- field = gtk_font_selection_get_xlfd_field (fontname, XLFD_PIXELS,
- field_buffer);
- size = atoi(field);
- if (size < 2)
- size = 2;
- fontsel->size = fontsel->selected_size = size;
- fontsel->metric = GTK_FONT_METRIC_PIXELS;
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(fontsel->pixels_button),
- TRUE);
- sprintf (buffer, "%i", size);
- }
- gtk_entry_set_text (GTK_ENTRY (fontsel->size_entry), buffer);
-
- /* Clear any current filter. */
- gtk_font_selection_clear_filter(fontsel);
-
- /* Now find the best style match. */
- fontsel->font_index = index;
- gtk_clist_select_row(GTK_CLIST(fontsel->font_clist), index, 0);
- if (GTK_WIDGET_MAPPED (fontsel->font_clist))
- gtk_clist_moveto(GTK_CLIST(fontsel->font_clist), index, -1, 0.5, 0);
-
- gtk_font_selection_show_available_styles (fontsel);
- /* This will load the font. */
- gtk_font_selection_select_best_style (fontsel, FALSE);
-
- return TRUE;
-}
+ new_desc = pango_font_description_from_string (fontname);
+ /* Check to make sure that this is in the list of allowed fonts */
-/* Returns the index of the given family, or -1 if not found */
-static gint
-gtk_font_selection_find_font (GtkFontSelection *fontsel,
- gchar *family,
- guint16 foundry)
-{
- FontInfo *font_info;
- gint lower, upper, middle = -1, cmp, nfonts;
- gint found_family = -1;
-
- font_info = fontsel_info->font_info;
- nfonts = fontsel_info->nfonts;
- if (nfonts == 0)
- return -1;
-
- /* Do a binary search to find the font family. */
- lower = 0;
- upper = nfonts;
- while (lower < upper)
+ pango_context_list_fonts (fontsel->context, new_desc->family_name, &descs, &n_descs);
+
+ for (i=0; i<n_descs; i++)
{
- middle = (lower + upper) >> 1;
-
- cmp = strcmp (family, font_info[middle].family);
- if (cmp == 0)
+ if (descs[i]->weight == new_desc->weight &&
+ descs[i]->style == new_desc->style &&
+ descs[i]->stretch == new_desc->stretch &&
+ descs[i]->variant == new_desc->variant)
{
- found_family = middle;
- cmp = strcmp(fontsel_info->properties[FOUNDRY][foundry],
- fontsel_info->properties[FOUNDRY][font_info[middle].foundry]);
+ found = TRUE;
+ break;
}
-
- if (cmp == 0)
- return middle;
- else if (cmp < 0)
- upper = middle;
- else if (cmp > 0)
- lower = middle+1;
}
-
- /* We couldn't find the family and foundry, but we may have just found the
- family, so we return that. */
- return found_family;
+
+ pango_font_descriptions_free (descs, n_descs);
+
+ if (!found)
+ return FALSE;
+
+ pango_font_description_free (fontsel->font_desc);
+ fontsel->font_desc = new_desc;
+
+ return TRUE;
}
@@ -3541,7 +864,7 @@ gtk_font_selection_find_font (GtkFontSelection *fontsel,
gchar*
gtk_font_selection_get_preview_text (GtkFontSelection *fontsel)
{
- return gtk_entry_get_text(GTK_ENTRY(fontsel->preview_entry));
+ return gtk_entry_get_text (GTK_ENTRY (fontsel->preview_entry));
}
@@ -3550,170 +873,9 @@ void
gtk_font_selection_set_preview_text (GtkFontSelection *fontsel,
const gchar *text)
{
- gtk_entry_set_text(GTK_ENTRY(fontsel->preview_entry), text);
+ gtk_entry_set_text (GTK_ENTRY (fontsel->preview_entry), text);
}
-
-/*****************************************************************************
- * These functions all deal with X Logical Font Description (XLFD) fontnames.
- * See the freely available documentation about this.
- *****************************************************************************/
-
-/*
- * Returns TRUE if the fontname is a valid XLFD.
- * (It just checks if the number of dashes is 14, and that each
- * field < XLFD_MAX_FIELD_LEN characters long - that's not in the XLFD but it
- * makes it easier for me).
- */
-static gboolean
-gtk_font_selection_is_xlfd_font_name (const gchar *fontname)
-{
- gint i = 0;
- gint field_len = 0;
-
- while (*fontname)
- {
- if (*fontname++ == '-')
- {
- if (field_len > XLFD_MAX_FIELD_LEN) return FALSE;
- field_len = 0;
- i++;
- }
- else
- field_len++;
- }
-
- return (i == 14) ? TRUE : FALSE;
-}
-
-/*
- * This fills the buffer with the specified field from the X Logical Font
- * Description name, and returns it. If fontname is NULL or the field is
- * longer than XFLD_MAX_FIELD_LEN it returns NULL.
- * Note: For the charset field, we also return the encoding, e.g. 'iso8859-1'.
- */
-static gchar*
-gtk_font_selection_get_xlfd_field (const gchar *fontname,
- FontField field_num,
- gchar *buffer)
-{
- const gchar *t1, *t2;
- gint countdown, len, num_dashes;
-#ifdef GDK_WINDOWING_WIN32
- gchar *p;
-#endif
-
- if (!fontname)
- return NULL;
-
- /* we assume this is a valid fontname...that is, it has 14 fields */
-
- countdown = field_num;
- t1 = fontname;
- while (*t1 && (countdown >= 0))
- if (*t1++ == '-')
- countdown--;
-
- num_dashes = (field_num == XLFD_CHARSET) ? 2 : 1;
- for (t2 = t1; *t2; t2++)
- {
- if (*t2 == '-' && --num_dashes == 0)
- break;
- }
-
- if (t1 != t2)
- {
- /* Check we don't overflow the buffer */
- len = (long) t2 - (long) t1;
- if (len > XLFD_MAX_FIELD_LEN - 1)
- return NULL;
- strncpy (buffer, t1, len);
- buffer[len] = 0;
-#ifdef GDK_WINDOWING_X11
- /* Convert to lower case. */
- g_strdown (buffer);
-#elif defined (GDK_WINDOWING_WIN32)
- /* Check for hex escapes in font family */
- if (field_num == XLFD_FAMILY)
- {
- p = buffer;
- while (*p)
- {
- if (*p == '%' && isxdigit (p[1]) && isxdigit (p[2]))
- {
- guint c;
- sscanf (p+1, "%2x", &c);
- *p = c;
- strcpy (p+1, p+3);
- }
- p++;
- }
- }
-#endif
- }
- else
- strcpy(buffer, "(nil)");
-
- return buffer;
-}
-
-/*
- * This returns a X Logical Font Description font name, given all the pieces.
- * Note: this retval must be freed by the caller.
- */
-static gchar *
-gtk_font_selection_create_xlfd (gint size,
- GtkFontMetricType metric,
- gchar *foundry,
- gchar *family,
- gchar *weight,
- gchar *slant,
- gchar *set_width,
- gchar *spacing,
- gchar *charset)
-{
- gchar buffer[16];
- gchar *pixel_size = "*", *point_size = "*", *fontname;
- gchar *fam = family;
-#ifdef GDK_WINDOWING_WIN32
- gchar *p, *q;
-#endif
-
- if (size <= 0)
- return NULL;
-
- sprintf (buffer, "%d", (int) size);
- if (metric == GTK_FONT_METRIC_PIXELS)
- pixel_size = buffer;
- else
- point_size = buffer;
-
-#ifdef GDK_WINDOWING_WIN32
- fam = g_malloc (strlen (family) * 3 + 1);
- p = fam;
- q = family;
- while (*q)
- {
- if (*q == '-' || *q == '*' || *q == '?' || *q == '%')
- p += sprintf (p, "%%%.02x", *q);
- else
- *p++ = *q;
- q++;
- }
- *p = '\0';
-#endif
- fontname = g_strdup_printf ("-%s-%s-%s-%s-%s-*-%s-%s-*-*-%s-*-%s",
- foundry, fam, weight, slant,
- set_width, pixel_size, point_size,
- spacing, charset);
-#ifdef GDK_WINDOWING_WIN32
- g_free (fam);
-#endif
- return fontname;
-}
-
-
-
/*****************************************************************************
* GtkFontSelectionDialog
*****************************************************************************/
@@ -3772,6 +934,7 @@ gtk_font_selection_dialog_init (GtkFontSelectionDialog *fontseldiag)
gtk_container_add (GTK_CONTAINER (fontseldiag), fontseldiag->main_vbox);
fontseldiag->fontsel = gtk_font_selection_new();
+ gtk_container_set_border_width (GTK_CONTAINER (fontseldiag->fontsel), 4);
gtk_widget_show (fontseldiag->fontsel);
gtk_box_pack_start (GTK_BOX (fontseldiag->main_vbox),
fontseldiag->fontsel, TRUE, TRUE, 0);
@@ -3839,23 +1002,6 @@ gtk_font_selection_dialog_set_font_name (GtkFontSelectionDialog *fsd,
fontname);
}
-void
-gtk_font_selection_dialog_set_filter (GtkFontSelectionDialog *fsd,
- GtkFontFilterType filter_type,
- GtkFontType font_type,
- gchar **foundries,
- gchar **weights,
- gchar **slants,
- gchar **setwidths,
- gchar **spacings,
- gchar **charsets)
-{
- gtk_font_selection_set_filter (GTK_FONT_SELECTION (fsd->fontsel),
- filter_type, font_type,
- foundries, weights, slants, setwidths,
- spacings, charsets);
-}
-
gchar*
gtk_font_selection_dialog_get_preview_text (GtkFontSelectionDialog *fsd)
{
diff --git a/gtk/gtkfontsel.h b/gtk/gtkfontsel.h
index fbd3ac3a5..8f7cc816e 100644
--- a/gtk/gtkfontsel.h
+++ b/gtk/gtkfontsel.h
@@ -34,7 +34,7 @@
#include <gdk/gdk.h>
#include <gtk/gtkwindow.h>
-#include <gtk/gtknotebook.h>
+#include <gtk/gtkvbox.h>
#ifdef __cplusplus
@@ -63,64 +63,10 @@ typedef struct _GtkFontSelectionClass GtkFontSelectionClass;
typedef struct _GtkFontSelectionDialog GtkFontSelectionDialog;
typedef struct _GtkFontSelectionDialogClass GtkFontSelectionDialogClass;
-
-
-
-/* This is the number of properties which we keep in the properties array,
- i.e. Weight, Slant, Set Width, Spacing, Charset & Foundry. */
-#define GTK_NUM_FONT_PROPERTIES 6
-
-/* This is the number of properties each style has i.e. Weight, Slant,
- Set Width, Spacing & Charset. Note that Foundry is not included,
- since it is the same for all styles of the same FontInfo. */
-#define GTK_NUM_STYLE_PROPERTIES 5
-
-
-/* Used to determine whether we are using point or pixel sizes. */
-typedef enum
-{
- GTK_FONT_METRIC_PIXELS,
- GTK_FONT_METRIC_POINTS
-} GtkFontMetricType;
-
-/* Used for determining the type of a font style, and also for setting filters.
- These can be combined if a style has bitmaps and scalable fonts available.*/
-typedef enum
-{
- GTK_FONT_BITMAP = 1 << 0,
- GTK_FONT_SCALABLE = 1 << 1,
- GTK_FONT_SCALABLE_BITMAP = 1 << 2,
-
- GTK_FONT_ALL = 0x07
-} GtkFontType;
-
-/* These are the two types of filter available - base and user. The base
- filter is set by the application and can't be changed by the user. */
-#define GTK_NUM_FONT_FILTERS 2
-typedef enum
-{
- GTK_FONT_FILTER_BASE,
- GTK_FONT_FILTER_USER
-} GtkFontFilterType;
-
-/* These hold the arrays of current filter settings for each property.
- If nfilters is 0 then all values of the property are OK. If not the
- filters array contains the indexes of the valid property values. */
-typedef struct _GtkFontFilter GtkFontFilter;
-struct _GtkFontFilter
-{
- gint font_type;
- guint16 *property_filters[GTK_NUM_FONT_PROPERTIES];
- guint16 property_nfilters[GTK_NUM_FONT_PROPERTIES];
-};
-
-
struct _GtkFontSelection
{
- GtkNotebook notebook;
+ GtkVBox parent_instance;
- /* These are on the font page. */
- GtkWidget *main_vbox;
GtkWidget *font_label;
GtkWidget *font_entry;
GtkWidget *font_clist;
@@ -132,44 +78,17 @@ struct _GtkFontSelection
GtkWidget *points_button;
GtkWidget *filter_button;
GtkWidget *preview_entry;
- GtkWidget *message_label;
-
- /* These are on the font info page. */
- GtkWidget *info_vbox;
- GtkWidget *info_clist;
- GtkWidget *requested_font_name;
- GtkWidget *actual_font_name;
-
- /* These are on the filter page. */
- GtkWidget *filter_vbox;
- GtkWidget *type_bitmaps_button;
- GtkWidget *type_scalable_button;
- GtkWidget *type_scaled_bitmaps_button;
- GtkWidget *filter_clists[GTK_NUM_FONT_PROPERTIES];
-
- GdkFont *font;
- gint font_index;
- gint style;
- GtkFontMetricType metric;
- /* The size is either in pixels or deci-points, depending on the metric. */
- gint size;
-
- /* This is the last size explicitly selected. When the user selects different
- fonts we try to find the nearest size to this. */
- gint selected_size;
-
- /* These are the current property settings. They are indexes into the
- strings in the GtkFontSelInfo properties array. */
- guint16 property_values[GTK_NUM_STYLE_PROPERTIES];
-
- /* These are the base and user font filters. */
- GtkFontFilter filters[GTK_NUM_FONT_FILTERS];
-};
+ PangoContext *context;
+ PangoFontDescription *font_desc;
+ GdkFont *font; /* Cache for gdk_font_selection_get_font, so we can preserve
+ * refcounting behavior
+ */
+};
struct _GtkFontSelectionClass
{
- GtkNotebookClass parent_class;
+ GtkVBoxClass parent_class;
};
@@ -209,15 +128,6 @@ gchar* gtk_font_selection_get_font_name (GtkFontSelection *fontsel);
GdkFont* gtk_font_selection_get_font (GtkFontSelection *fontsel);
gboolean gtk_font_selection_set_font_name (GtkFontSelection *fontsel,
const gchar *fontname);
-void gtk_font_selection_set_filter (GtkFontSelection *fontsel,
- GtkFontFilterType filter_type,
- GtkFontType font_type,
- gchar **foundries,
- gchar **weights,
- gchar **slants,
- gchar **setwidths,
- gchar **spacings,
- gchar **charsets);
gchar* gtk_font_selection_get_preview_text (GtkFontSelection *fontsel);
void gtk_font_selection_set_preview_text (GtkFontSelection *fontsel,
const gchar *text);
@@ -252,38 +162,6 @@ GdkFont* gtk_font_selection_dialog_get_font (GtkFontSelectionDialog *fsd);
gboolean gtk_font_selection_dialog_set_font_name (GtkFontSelectionDialog *fsd,
const gchar *fontname);
-/* This sets one of the font filters, to limit the fonts shown. The filter_type
- is GTK_FONT_FILTER_BASE or GTK_FONT_FILTER_USER. The font type is a
- combination of the bit flags GTK_FONT_BITMAP, GTK_FONT_SCALABLE and
- GTK_FONT_SCALABLE_BITMAP (or GTK_FONT_ALL for all font types).
- The foundries, weights etc. are arrays of strings containing property
- values, e.g. 'bold', 'demibold', and *MUST* finish with a NULL.
- Standard long names are also accepted, e.g. 'italic' instead of 'i'.
-
- e.g. to allow only fixed-width fonts ('char cell' or 'monospaced') to be
- selected use:
-
- gchar *spacings[] = { "c", "m", NULL };
- gtk_font_selection_dialog_set_filter (GTK_FONT_SELECTION_DIALOG (fontsel),
- GTK_FONT_FILTER_BASE, GTK_FONT_ALL,
- NULL, NULL, NULL, NULL, spacings, NULL);
-
- to allow only true scalable fonts to be selected use:
-
- gtk_font_selection_dialog_set_filter (GTK_FONT_SELECTION_DIALOG (fontsel),
- GTK_FONT_FILTER_BASE, GTK_FONT_SCALABLE,
- NULL, NULL, NULL, NULL, NULL, NULL);
-*/
-void gtk_font_selection_dialog_set_filter (GtkFontSelectionDialog *fsd,
- GtkFontFilterType filter_type,
- GtkFontType font_type,
- gchar **foundries,
- gchar **weights,
- gchar **slants,
- gchar **setwidths,
- gchar **spacings,
- gchar **charsets);
-
/* This returns the text in the preview entry. You should copy the returned
text if you need it. */
gchar* gtk_font_selection_dialog_get_preview_text (GtkFontSelectionDialog *fsd);
diff --git a/gtk/gtkframe.c b/gtk/gtkframe.c
index 453519efa..03f1be3fb 100644
--- a/gtk/gtkframe.c
+++ b/gtk/gtkframe.c
@@ -26,6 +26,7 @@
#include <string.h>
#include "gtkframe.h"
+#include "gtklabel.h"
enum {
ARG_0,
@@ -44,7 +45,6 @@ static void gtk_frame_set_arg (GtkObject *object,
static void gtk_frame_get_arg (GtkObject *object,
GtkArg *arg,
guint arg_id);
-static void gtk_frame_finalize (GObject *object);
static void gtk_frame_paint (GtkWidget *widget,
GdkRectangle *area);
static void gtk_frame_draw (GtkWidget *widget,
@@ -55,9 +55,19 @@ static void gtk_frame_size_request (GtkWidget *widget,
GtkRequisition *requisition);
static void gtk_frame_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
-static void gtk_frame_style_set (GtkWidget *widget,
- GtkStyle *previous_style);
-
+static void gtk_frame_map (GtkWidget *widget);
+static void gtk_frame_unmap (GtkWidget *widget);
+static void gtk_frame_remove (GtkContainer *container,
+ GtkWidget *child);
+static void gtk_frame_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data);
+
+static void gtk_frame_compute_child_allocation (GtkFrame *frame,
+ GtkAllocation *child_allocation);
+static void gtk_frame_real_compute_child_allocation (GtkFrame *frame,
+ GtkAllocation *child_allocation);
static GtkBinClass *parent_class = NULL;
@@ -90,17 +100,16 @@ gtk_frame_get_type (void)
static void
gtk_frame_class_init (GtkFrameClass *class)
{
- GObjectClass *gobject_class = G_OBJECT_CLASS (class);
GtkObjectClass *object_class;
GtkWidgetClass *widget_class;
+ GtkContainerClass *container_class;
- object_class = (GtkObjectClass*) class;
- widget_class = (GtkWidgetClass*) class;
+ object_class = GTK_OBJECT_CLASS (class);
+ widget_class = GTK_WIDGET_CLASS (class);
+ container_class = GTK_CONTAINER_CLASS (class);
parent_class = gtk_type_class (gtk_bin_get_type ());
- gobject_class->finalize = gtk_frame_finalize;
-
gtk_object_add_arg_type ("GtkFrame::label", GTK_TYPE_STRING, GTK_ARG_READWRITE, ARG_LABEL);
gtk_object_add_arg_type ("GtkFrame::label_xalign", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_LABEL_XALIGN);
gtk_object_add_arg_type ("GtkFrame::label_yalign", GTK_TYPE_FLOAT, GTK_ARG_READWRITE, ARG_LABEL_YALIGN);
@@ -113,16 +122,20 @@ gtk_frame_class_init (GtkFrameClass *class)
widget_class->expose_event = gtk_frame_expose;
widget_class->size_request = gtk_frame_size_request;
widget_class->size_allocate = gtk_frame_size_allocate;
- widget_class->style_set = gtk_frame_style_set;
+ widget_class->map = gtk_frame_map;
+ widget_class->unmap = gtk_frame_unmap;
+
+ container_class->remove = gtk_frame_remove;
+ container_class->forall = gtk_frame_forall;
+
+ class->compute_child_allocation = gtk_frame_real_compute_child_allocation;
}
static void
gtk_frame_init (GtkFrame *frame)
{
- frame->label = NULL;
+ frame->label_widget = NULL;
frame->shadow_type = GTK_SHADOW_ETCHED_IN;
- frame->label_width = 0;
- frame->label_height = 0;
frame->label_xalign = 0.0;
frame->label_yalign = 0.5;
}
@@ -167,7 +180,7 @@ gtk_frame_get_arg (GtkObject *object,
switch (arg_id)
{
case ARG_LABEL:
- GTK_VALUE_STRING (*arg) = g_strdup (frame->label);
+ GTK_VALUE_STRING (*arg) = gtk_frame_get_label (frame);
break;
case ARG_LABEL_XALIGN:
GTK_VALUE_FLOAT (*arg) = frame->label_xalign;
@@ -197,22 +210,31 @@ gtk_frame_new (const gchar *label)
}
static void
-gtk_frame_style_set (GtkWidget *widget,
- GtkStyle *previous_style)
+gtk_frame_remove (GtkContainer *container,
+ GtkWidget *child)
{
- GtkFrame *frame;
+ GtkFrame *frame = GTK_FRAME (container);
- frame = GTK_FRAME (widget);
+ if (frame->label_widget == child)
+ gtk_frame_set_label_widget (frame, NULL);
+ else
+ GTK_CONTAINER_CLASS (parent_class)->remove (container, child);
+}
- if (frame->label)
- {
- frame->label_width = gdk_string_measure (GTK_WIDGET (frame)->style->font, frame->label) + 7;
- frame->label_height = (GTK_WIDGET (frame)->style->font->ascent +
- GTK_WIDGET (frame)->style->font->descent + 1);
- }
+static void
+gtk_frame_forall (GtkContainer *container,
+ gboolean include_internals,
+ GtkCallback callback,
+ gpointer callback_data)
+{
+ GtkBin *bin = GTK_BIN (container);
+ GtkFrame *frame = GTK_FRAME (container);
+
+ if (bin->child)
+ (* callback) (bin->child, callback_data);
- if (GTK_WIDGET_CLASS (parent_class)->style_set)
- GTK_WIDGET_CLASS (parent_class)->style_set (widget, previous_style);
+ if (frame->label_widget)
+ (* callback) (frame->label_widget, callback_data);
}
void
@@ -222,43 +244,84 @@ gtk_frame_set_label (GtkFrame *frame,
g_return_if_fail (frame != NULL);
g_return_if_fail (GTK_IS_FRAME (frame));
- if ((label && frame->label && (strcmp (frame->label, label) == 0)) ||
- (!label && !frame->label))
- return;
-
- if (frame->label)
- g_free (frame->label);
- frame->label = NULL;
-
- if (label)
+ if (!label)
{
- frame->label = g_strdup (label);
- frame->label_width = gdk_string_measure (GTK_WIDGET (frame)->style->font, frame->label) + 7;
- frame->label_height = (GTK_WIDGET (frame)->style->font->ascent +
- GTK_WIDGET (frame)->style->font->descent + 1);
+ gtk_frame_set_label_widget (frame, NULL);
}
else
{
- frame->label_width = 0;
- frame->label_height = 0;
+ GtkWidget *child = gtk_label_new (label);
+ gtk_widget_show (child);
+
+ gtk_frame_set_label_widget (frame, child);
}
+}
- if (GTK_WIDGET_DRAWABLE (frame))
- {
- GtkWidget *widget;
+/**
+ * gtk_frame_get_label:
+ * @frame: a #GtkFrame
+ *
+ * If the frame's label widget is a #GtkLabel, return the
+ * text in the label widget. (The frame will have a #GtkLabel
+ * for the label widget if a non-%NULL argument was passed
+ * to gtk_frame_new().)
+ *
+ * Return value: the text in the label, or %NULL if there
+ * was no label widget or the lable widget was not
+ * a #GtkLabel. This value must be freed with g_free().
+ **/
+gchar *
+gtk_frame_get_label (GtkFrame *frame)
+{
+ g_return_val_if_fail (frame != NULL, NULL);
+ g_return_val_if_fail (GTK_IS_FRAME (frame), NULL);
- /* clear the old label area
- */
- widget = GTK_WIDGET (frame);
- gtk_widget_queue_clear_area (widget,
- widget->allocation.x + GTK_CONTAINER (frame)->border_width,
- widget->allocation.y + GTK_CONTAINER (frame)->border_width,
- widget->allocation.width - GTK_CONTAINER (frame)->border_width,
- widget->allocation.y + frame->label_height);
+ if (frame->label_widget && GTK_IS_LABEL (frame->label_widget))
+ return gtk_label_get_text (GTK_LABEL (frame->label_widget));
+ else
+ return NULL;
+}
- }
+/**
+ * gtk_frame_set_label_widget:
+ * @frame: a #GtkFrame
+ * @label_widget: the new label widget
+ *
+ * Set the label widget for the frame. This is the widget that
+ * will appear embedded in the top edge of the frame as a
+ * title.
+ **/
+void
+gtk_frame_set_label_widget (GtkFrame *frame,
+ GtkWidget *label_widget)
+{
+ gboolean need_resize = FALSE;
+
+ g_return_if_fail (frame != NULL);
+ g_return_if_fail (GTK_IS_FRAME (frame));
+ g_return_if_fail (label_widget == NULL || GTK_IS_WIDGET (label_widget));
+ g_return_if_fail (label_widget == NULL || label_widget->parent == NULL);
- gtk_widget_queue_resize (GTK_WIDGET (frame));
+ if (frame->label_widget == label_widget)
+ return;
+
+ if (frame->label_widget)
+ {
+ need_resize = GTK_WIDGET_VISIBLE (frame->label_widget);
+ gtk_widget_unparent (frame->label_widget);
+ }
+
+ frame->label_widget = label_widget;
+
+ if (label_widget)
+ {
+ frame->label_widget = label_widget;
+ gtk_widget_set_parent (label_widget, GTK_WIDGET (frame));
+ need_resize |= GTK_WIDGET_VISIBLE (label_widget);
+ }
+
+ if (GTK_WIDGET_VISIBLE (frame) && need_resize)
+ gtk_widget_queue_resize (GTK_WIDGET (frame));
}
void
@@ -277,20 +340,6 @@ gtk_frame_set_label_align (GtkFrame *frame,
frame->label_xalign = xalign;
frame->label_yalign = yalign;
- if (GTK_WIDGET_DRAWABLE (frame))
- {
- GtkWidget *widget;
-
- /* clear the old label area
- */
- widget = GTK_WIDGET (frame);
- gtk_widget_queue_clear_area (widget,
- widget->allocation.x + GTK_CONTAINER (frame)->border_width,
- widget->allocation.y + GTK_CONTAINER (frame)->border_width,
- widget->allocation.width - GTK_CONTAINER (frame)->border_width,
- widget->allocation.y + frame->label_height);
-
- }
gtk_widget_queue_resize (GTK_WIDGET (frame));
}
}
@@ -314,79 +363,54 @@ gtk_frame_set_shadow_type (GtkFrame *frame,
}
}
-
-static void
-gtk_frame_finalize (GObject *object)
-{
- GtkFrame *frame;
-
- g_return_if_fail (GTK_IS_FRAME (object));
-
- frame = GTK_FRAME (object);
-
- if (frame->label)
- g_free (frame->label);
-
- G_OBJECT_CLASS (parent_class)->finalize (object);
-}
-
static void
gtk_frame_paint (GtkWidget *widget,
GdkRectangle *area)
{
GtkFrame *frame;
- gint height_extra;
- gint label_area_width;
- gint x, y, x2, y2;
-
- g_return_if_fail (widget != NULL);
- g_return_if_fail (GTK_IS_FRAME (widget));
- g_return_if_fail (area != NULL);
+ gint x, y, width, height;
if (GTK_WIDGET_DRAWABLE (widget))
{
frame = GTK_FRAME (widget);
- height_extra = frame->label_height - widget->style->klass->xthickness;
- height_extra = MAX (height_extra, 0);
+ x = frame->child_allocation.x - widget->style->klass->xthickness;
+ y = frame->child_allocation.y - widget->style->klass->ythickness;
+ width = frame->child_allocation.width + 2 * widget->style->klass->xthickness;
+ height = frame->child_allocation.height + 2 * widget->style->klass->ythickness;
- x = GTK_CONTAINER (frame)->border_width;
- y = GTK_CONTAINER (frame)->border_width;
-
- if (frame->label)
+ if (frame->label_widget)
{
- label_area_width = (widget->allocation.width -
- GTK_CONTAINER (frame)->border_width * 2 -
- widget->style->klass->xthickness * 2);
-
- x2 = ((label_area_width - frame->label_width) * frame->label_xalign +
- GTK_CONTAINER (frame)->border_width + widget->style->klass->xthickness);
- y2 = (GTK_CONTAINER (frame)->border_width + widget->style->font->ascent);
-
- gtk_paint_shadow_gap (widget->style, widget->window,
- GTK_STATE_NORMAL, frame->shadow_type,
- area, widget, "frame",
- widget->allocation.x + x,
- widget->allocation.y + y + height_extra / 2,
- widget->allocation.width - x * 2,
- widget->allocation.height - y * 2 - height_extra / 2,
- GTK_POS_TOP,
- x2 + 2 - x, frame->label_width - 4);
-
- gtk_paint_string (widget->style, widget->window, GTK_WIDGET_STATE (widget),
- area, widget, "frame",
- widget->allocation.x + x2 + 3,
- widget->allocation.y + y2,
- frame->label);
+ GtkRequisition child_requisition;
+ gfloat xalign;
+ gint height_extra;
+ gint x2;
+
+ gtk_widget_get_child_requisition (frame->label_widget, &child_requisition);
+
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+ xalign = frame->label_xalign;
+ else
+ xalign = 1 - frame->label_xalign;
+
+ height_extra = MAX (0, child_requisition.height - widget->style->klass->xthickness);
+ y -= height_extra * (1 - frame->label_yalign);
+ height += height_extra * (1 - frame->label_yalign);
+
+ x2 = 2 + (frame->child_allocation.width - child_requisition.width) * xalign;
+
+ gtk_paint_shadow_gap (widget->style, widget->window,
+ GTK_STATE_NORMAL, frame->shadow_type,
+ area, widget, "frame",
+ x, y, width, height,
+ GTK_POS_TOP,
+ x2, child_requisition.width - 4);
}
else
gtk_paint_shadow (widget->style, widget->window,
GTK_STATE_NORMAL, frame->shadow_type,
area, widget, "frame",
- widget->allocation.x + x,
- widget->allocation.y + y + height_extra / 2,
- widget->allocation.width - x * 2,
- widget->allocation.height - y * 2 - height_extra / 2);
+ x, y, width, height);
}
}
@@ -394,46 +418,47 @@ static void
gtk_frame_draw (GtkWidget *widget,
GdkRectangle *area)
{
- GtkBin *bin;
+ GtkBin *bin = GTK_BIN (widget);
+ GtkFrame *frame = GTK_FRAME (widget);
GdkRectangle child_area;
- g_return_if_fail (widget != NULL);
- g_return_if_fail (GTK_IS_FRAME (widget));
- g_return_if_fail (area != NULL);
-
if (GTK_WIDGET_DRAWABLE (widget))
{
- bin = GTK_BIN (widget);
-
gtk_frame_paint (widget, area);
if (bin->child && gtk_widget_intersect (bin->child, area, &child_area))
gtk_widget_draw (bin->child, &child_area);
+
+ if (frame->label_widget && gtk_widget_intersect (frame->label_widget, area, &child_area))
+ gtk_widget_draw (frame->label_widget, &child_area);
}
}
-static gint
+static gboolean
gtk_frame_expose (GtkWidget *widget,
GdkEventExpose *event)
{
- GtkBin *bin;
+ GtkBin *bin = GTK_BIN (widget);
+ GtkFrame *frame = GTK_FRAME (widget);
GdkEventExpose child_event;
- g_return_val_if_fail (widget != NULL, FALSE);
- g_return_val_if_fail (GTK_IS_FRAME (widget), FALSE);
- g_return_val_if_fail (event != NULL, FALSE);
-
if (GTK_WIDGET_DRAWABLE (widget))
{
- bin = GTK_BIN (widget);
-
gtk_frame_paint (widget, &event->area);
- child_event = *event;
- if (bin->child &&
- GTK_WIDGET_NO_WINDOW (bin->child) &&
- gtk_widget_intersect (bin->child, &event->area, &child_event.area))
- gtk_widget_event (bin->child, (GdkEvent*) &child_event);
+ if (bin->child && GTK_WIDGET_NO_WINDOW (bin->child))
+ {
+ child_event = *event;
+ if (gtk_widget_intersect (bin->child, &event->area, &child_event.area))
+ gtk_widget_event (bin->child, (GdkEvent*) &child_event);
+ }
+
+ if (frame->label_widget && GTK_WIDGET_NO_WINDOW (frame->label_widget))
+ {
+ child_event = *event;
+ if (gtk_widget_intersect (frame->label_widget, &event->area, &child_event.area))
+ gtk_widget_event (frame->label_widget, (GdkEvent*) &child_event);
+ }
}
return FALSE;
@@ -443,82 +468,154 @@ static void
gtk_frame_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
- GtkFrame *frame;
- GtkBin *bin;
- gint tmp_height;
+ GtkFrame *frame = GTK_FRAME (widget);
+ GtkBin *bin = GTK_BIN (widget);
+ GtkRequisition child_requisition;
+
+ if (frame->label_widget && GTK_WIDGET_VISIBLE (frame->label_widget))
+ {
+ gtk_widget_size_request (frame->label_widget, &child_requisition);
- g_return_if_fail (widget != NULL);
- g_return_if_fail (GTK_IS_FRAME (widget));
- g_return_if_fail (requisition != NULL);
+ requisition->width = child_requisition.width;
+ requisition->height =
+ MAX (0, child_requisition.height - GTK_WIDGET (widget)->style->klass->xthickness);
+ }
+ else
+ {
+ requisition->width = 0;
+ requisition->height = 0;
+ }
+
+ if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
+ {
+ gtk_widget_size_request (bin->child, &child_requisition);
- frame = GTK_FRAME (widget);
- bin = GTK_BIN (widget);
+ requisition->width = MAX (requisition->width, child_requisition.width);
+ requisition->height += child_requisition.height;
+ }
- requisition->width = (GTK_CONTAINER (widget)->border_width +
- GTK_WIDGET (widget)->style->klass->xthickness) * 2;
+ requisition->width += (GTK_CONTAINER (widget)->border_width +
+ GTK_WIDGET (widget)->style->klass->xthickness) * 2;
+ requisition->height += (GTK_CONTAINER (widget)->border_width +
+ GTK_WIDGET (widget)->style->klass->xthickness) * 2;
+}
- tmp_height = frame->label_height - GTK_WIDGET (widget)->style->klass->ythickness;
- tmp_height = MAX (tmp_height, 0);
+static void
+gtk_frame_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GtkFrame *frame = GTK_FRAME (widget);
+ GtkBin *bin = GTK_BIN (widget);
- requisition->height = tmp_height + (GTK_CONTAINER (widget)->border_width +
- GTK_WIDGET (widget)->style->klass->ythickness) * 2;
+ widget->allocation = *allocation;
if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
{
- GtkRequisition child_requisition;
+ GtkAllocation new_allocation;
- gtk_widget_size_request (bin->child, &child_requisition);
-
- requisition->width += MAX (child_requisition.width, frame->label_width);
- requisition->height += child_requisition.height;
+ gtk_frame_compute_child_allocation (frame, &new_allocation);
+
+ /* If the child allocation changed, that means that the frame is drawn
+ * in a new place, so we must redraw the entire widget.
+ */
+ if (GTK_WIDGET_MAPPED (widget) &&
+ (new_allocation.x != frame->child_allocation.x ||
+ new_allocation.y != frame->child_allocation.y ||
+ new_allocation.width != frame->child_allocation.width ||
+ new_allocation.height != frame->child_allocation.height))
+ gtk_widget_queue_clear (widget);
+
+ gtk_widget_size_allocate (bin->child, &new_allocation);
+ frame->child_allocation = new_allocation;
}
- else
+
+ if (frame->label_widget && GTK_WIDGET_VISIBLE (frame->label_widget))
{
- requisition->width += frame->label_width;
+ GtkRequisition child_requisition;
+ GtkAllocation child_allocation;
+ gfloat xalign;
+
+ gtk_widget_get_child_requisition (frame->label_widget, &child_requisition);
+
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+ xalign = frame->label_xalign;
+ else
+ xalign = 1 - frame->label_xalign;
+
+ child_allocation.x = frame->child_allocation.x +
+ (frame->child_allocation.width - child_requisition.width) * xalign;
+ child_allocation.width = child_requisition.width;
+
+ child_allocation.y = frame->child_allocation.y - child_requisition.height;
+ child_allocation.height = child_requisition.height;
+
+ gtk_widget_size_allocate (frame->label_widget, &child_allocation);
}
}
static void
-gtk_frame_size_allocate (GtkWidget *widget,
- GtkAllocation *allocation)
+gtk_frame_map (GtkWidget *widget)
{
- GtkFrame *frame;
- GtkBin *bin;
- GtkAllocation child_allocation;
+ GtkFrame *frame = GTK_FRAME (widget);
+
+ if (frame->label_widget &&
+ GTK_WIDGET_VISIBLE (frame->label_widget) &&
+ !GTK_WIDGET_MAPPED (frame->label_widget))
+ gtk_widget_map (frame->label_widget);
- g_return_if_fail (widget != NULL);
- g_return_if_fail (GTK_IS_FRAME (widget));
- g_return_if_fail (allocation != NULL);
+ if (GTK_WIDGET_CLASS (parent_class)->map)
+ (* GTK_WIDGET_CLASS (parent_class)->map) (widget);
+}
- frame = GTK_FRAME (widget);
- bin = GTK_BIN (widget);
+static void
+gtk_frame_unmap (GtkWidget *widget)
+{
+ GtkFrame *frame = GTK_FRAME (widget);
- if (GTK_WIDGET_MAPPED (widget) &&
- ((widget->allocation.x != allocation->x) ||
- (widget->allocation.y != allocation->y) ||
- (widget->allocation.width != allocation->width) ||
- (widget->allocation.height != allocation->height)) &&
- (widget->allocation.width != 0) &&
- (widget->allocation.height != 0))
- gtk_widget_queue_clear (widget);
+ if (GTK_WIDGET_CLASS (parent_class)->unmap)
+ (* GTK_WIDGET_CLASS (parent_class)->unmap) (widget);
- widget->allocation = *allocation;
+ if (frame->label_widget && GTK_WIDGET_MAPPED (frame->label_widget))
+ gtk_widget_unmap (frame->label_widget);
+}
- if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
- {
- child_allocation.x = (GTK_CONTAINER (frame)->border_width +
- GTK_WIDGET (frame)->style->klass->xthickness);
- child_allocation.width = MAX(1, (gint)allocation->width - child_allocation.x * 2);
+static void
+gtk_frame_compute_child_allocation (GtkFrame *frame,
+ GtkAllocation *child_allocation)
+{
+ g_return_if_fail (frame != NULL);
+ g_return_if_fail (GTK_IS_FRAME (frame));
+ g_return_if_fail (child_allocation != NULL);
- child_allocation.y = (GTK_CONTAINER (frame)->border_width +
- MAX (frame->label_height, GTK_WIDGET (frame)->style->klass->ythickness));
- child_allocation.height = MAX (1, ((gint)allocation->height - child_allocation.y -
- (gint)GTK_CONTAINER (frame)->border_width -
- (gint)GTK_WIDGET (frame)->style->klass->ythickness));
+ GTK_FRAME_GET_CLASS (frame)->compute_child_allocation (frame, child_allocation);
+}
- child_allocation.x += allocation->x;
- child_allocation.y += allocation->y;
+static void
+gtk_frame_real_compute_child_allocation (GtkFrame *frame,
+ GtkAllocation *child_allocation)
+{
+ GtkWidget *widget = GTK_WIDGET (frame);
+ GtkAllocation *allocation = &widget->allocation;
+ GtkRequisition child_requisition;
+ gint top_margin;
- gtk_widget_size_allocate (bin->child, &child_allocation);
+ if (frame->label_widget)
+ {
+ gtk_widget_get_child_requisition (frame->label_widget, &child_requisition);
+ top_margin = MAX (child_requisition.height, widget->style->klass->ythickness);
}
+ else
+ top_margin = widget->style->klass->ythickness;
+
+ child_allocation->x = (GTK_CONTAINER (frame)->border_width +
+ widget->style->klass->xthickness);
+ child_allocation->width = MAX(1, (gint)allocation->width - child_allocation->x * 2);
+
+ child_allocation->y = (GTK_CONTAINER (frame)->border_width + top_margin);
+ child_allocation->height = MAX (1, ((gint)allocation->height - child_allocation->y -
+ (gint)GTK_CONTAINER (frame)->border_width -
+ (gint)widget->style->klass->ythickness));
+
+ child_allocation->x += allocation->x;
+ child_allocation->y += allocation->y;
}
diff --git a/gtk/gtkframe.h b/gtk/gtkframe.h
index 7e6dc56dd..caeb47bd5 100644
--- a/gtk/gtkframe.h
+++ b/gtk/gtkframe.h
@@ -51,31 +51,35 @@ typedef struct _GtkFrameClass GtkFrameClass;
struct _GtkFrame
{
GtkBin bin;
-
- gchar *label;
+
+ GtkWidget *label_widget;
gint16 shadow_type;
- gint16 label_width;
- gint16 label_height;
gfloat label_xalign;
gfloat label_yalign;
+
+ GtkAllocation child_allocation;
};
struct _GtkFrameClass
{
GtkBinClass parent_class;
-};
+ void (*compute_child_allocation) (GtkFrame *frame, GtkAllocation *allocation);
+};
-GtkType gtk_frame_get_type (void);
-GtkWidget* gtk_frame_new (const gchar *label);
-void gtk_frame_set_label (GtkFrame *frame,
- const gchar *label);
-void gtk_frame_set_label_align (GtkFrame *frame,
- gfloat xalign,
- gfloat yalign);
-void gtk_frame_set_shadow_type (GtkFrame *frame,
- GtkShadowType type);
+GtkType gtk_frame_get_type (void);
+GtkWidget* gtk_frame_new (const gchar *label);
+void gtk_frame_set_label (GtkFrame *frame,
+ const gchar *label);
+gchar * gtk_frame_get_label (GtkFrame *frame);
+void gtk_frame_set_label_widget (GtkFrame *frame,
+ GtkWidget *title_widget);
+void gtk_frame_set_label_align (GtkFrame *frame,
+ gfloat xalign,
+ gfloat yalign);
+void gtk_frame_set_shadow_type (GtkFrame *frame,
+ GtkShadowType type);
#ifdef __cplusplus
}
diff --git a/gtk/gtkhbbox.c b/gtk/gtkhbbox.c
index 8ba84d708..a86652ebf 100644
--- a/gtk/gtkhbbox.c
+++ b/gtk/gtkhbbox.c
@@ -272,8 +272,14 @@ gtk_hbutton_box_size_allocate (GtkWidget *widget,
{
child_allocation.width = child_width;
child_allocation.height = child_height;
- child_allocation.x = x;
+
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+ child_allocation.x = x;
+ else
+ child_allocation.x = allocation->x + allocation->width - (x - allocation->x + child_width);
+
child_allocation.y = y;
+
gtk_widget_size_allocate (child->widget, &child_allocation);
x += childspace;
}
diff --git a/gtk/gtkhbox.c b/gtk/gtkhbox.c
index 89c96aa56..467c45109 100644
--- a/gtk/gtkhbox.c
+++ b/gtk/gtkhbox.c
@@ -163,6 +163,7 @@ gtk_hbox_size_allocate (GtkWidget *widget,
gint width;
gint extra;
gint x;
+ GtkTextDirection direction;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_HBOX (widget));
@@ -171,6 +172,8 @@ gtk_hbox_size_allocate (GtkWidget *widget,
box = GTK_BOX (widget);
widget->allocation = *allocation;
+ direction = gtk_widget_get_direction (widget);
+
nvis_children = 0;
nexpand_children = 0;
children = box->children;
@@ -264,6 +267,9 @@ gtk_hbox_size_allocate (GtkWidget *widget,
child_allocation.x = x + (child_width - child_allocation.width) / 2;
}
+ if (direction == GTK_TEXT_DIR_RTL)
+ child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
+
gtk_widget_size_allocate (child->widget, &child_allocation);
x += child_width + box->spacing;
@@ -320,6 +326,9 @@ gtk_hbox_size_allocate (GtkWidget *widget,
child_allocation.x = x + (child_width - child_allocation.width) / 2 - child_width;
}
+ if (direction == GTK_TEXT_DIR_RTL)
+ child_allocation.x = allocation->x + allocation->width - (child_allocation.x - allocation->x) - child_allocation.width;
+
gtk_widget_size_allocate (child->widget, &child_allocation);
x -= (child_width + box->spacing);
diff --git a/gtk/gtkhruler.c b/gtk/gtkhruler.c
index d827de748..701aeb213 100644
--- a/gtk/gtkhruler.c
+++ b/gtk/gtkhruler.c
@@ -148,8 +148,11 @@ gtk_hruler_draw_ticks (GtkRuler *ruler)
gfloat start, end, cur;
gchar unit_str[32];
gint digit_height;
+ gint digit_offset;
gint text_width;
gint pos;
+ PangoLayout *layout;
+ PangoRectangle logical_rect, ink_rect;
g_return_if_fail (ruler != NULL);
g_return_if_fail (GTK_IS_HRULER (ruler));
@@ -165,20 +168,28 @@ gtk_hruler_draw_ticks (GtkRuler *ruler)
xthickness = widget->style->klass->xthickness;
ythickness = widget->style->klass->ythickness;
- digit_height = font->ascent; /* assume descent == 0 ? */
+
+ digit_height = ink_rect.height / PANGO_SCALE + 2;
+ digit_offset = ink_rect.y;
+
+ layout = gtk_widget_create_pango_layout (widget);
+ pango_layout_set_text (layout, "012456789", -1);
+ pango_layout_get_extents (layout, &ink_rect, &logical_rect);
+
+ digit_height = ink_rect.height / PANGO_SCALE + 1;
+ digit_offset = ink_rect.y;
width = widget->allocation.width;
height = widget->allocation.height - ythickness * 2;
-
- gtk_paint_box (widget->style, ruler->backing_store,
- GTK_STATE_NORMAL, GTK_SHADOW_OUT,
- NULL, widget, "hruler",
- 0, 0,
- widget->allocation.width, widget->allocation.height);
-
-
- gdk_draw_line (ruler->backing_store, gc,
+ gtk_paint_box (widget->style, ruler->backing_store,
+ GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+ NULL, widget, "hruler",
+ 0, 0,
+ widget->allocation.width, widget->allocation.height);
+
+
+ gdk_draw_line (ruler->backing_store, gc,
xthickness,
height + ythickness,
widget->allocation.width - xthickness,
@@ -247,12 +258,18 @@ gtk_hruler_draw_ticks (GtkRuler *ruler)
if (i == 0)
{
sprintf (unit_str, "%d", (int) cur);
- gdk_draw_string (ruler->backing_store, font, gc,
- pos + 2, ythickness + font->ascent - 1,
- unit_str);
+
+ pango_layout_set_text (layout, unit_str, -1);
+ pango_layout_get_extents (layout, &logical_rect, NULL);
+
+ gdk_draw_layout (ruler->backing_store, gc,
+ pos + 2, ythickness + (logical_rect.y - digit_offset) / PANGO_SCALE,
+ layout);
}
}
}
+
+ pango_layout_unref (layout);
}
static void
diff --git a/gtk/gtkhscale.c b/gtk/gtkhscale.c
index 94ba294b0..30044b8bb 100644
--- a/gtk/gtkhscale.c
+++ b/gtk/gtkhscale.c
@@ -29,7 +29,6 @@
#include "gtksignal.h"
#include "gdk/gdkkeysyms.h"
-
#define SCALE_CLASS(w) GTK_SCALE_GET_CLASS (w)
#define RANGE_CLASS(w) GTK_RANGE_GET_CLASS (w)
@@ -314,7 +313,6 @@ gtk_hscale_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
GtkScale *scale;
- gint value_width;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_HSCALE (widget));
@@ -326,24 +324,25 @@ gtk_hscale_size_request (GtkWidget *widget,
widget->style->klass->xthickness) * 2;
requisition->height = (RANGE_CLASS (scale)->slider_width +
widget->style->klass->ythickness * 2);
-
+
if (scale->draw_value)
{
- value_width = gtk_scale_get_value_width (scale);
+ gint value_width, value_height;
+ gtk_scale_get_value_size (scale, &value_width, &value_height);
if ((scale->value_pos == GTK_POS_LEFT) ||
(scale->value_pos == GTK_POS_RIGHT))
{
requisition->width += value_width + SCALE_CLASS (scale)->value_spacing;
- if (requisition->height < (widget->style->font->ascent + widget->style->font->descent))
- requisition->height = widget->style->font->ascent + widget->style->font->descent;
+ if (requisition->height < value_height)
+ requisition->height = value_height;
}
else if ((scale->value_pos == GTK_POS_TOP) ||
(scale->value_pos == GTK_POS_BOTTOM))
{
if (requisition->width < value_width)
requisition->width = value_width;
- requisition->height += widget->style->font->ascent + widget->style->font->descent;
+ requisition->height += value_height;
}
}
}
@@ -398,23 +397,25 @@ gtk_hscale_pos_trough (GtkHScale *hscale,
if (scale->draw_value)
{
+ gint value_width, value_height;
+ gtk_scale_get_value_size (scale, &value_width, &value_height);
+
*x = 0;
*y = 0;
switch (scale->value_pos)
{
case GTK_POS_LEFT:
- *x += gtk_scale_get_value_width (scale) + SCALE_CLASS (scale)->value_spacing;
+ *x += value_width + SCALE_CLASS (scale)->value_spacing;
*y = (widget->allocation.height - *h) / 2;
*w -= *x;
break;
case GTK_POS_RIGHT:
- *w -= gtk_scale_get_value_width (scale) + SCALE_CLASS (scale)->value_spacing;
+ *w -= value_width + SCALE_CLASS (scale)->value_spacing;
*y = (widget->allocation.height - *h) / 2;
break;
case GTK_POS_TOP:
- *y = (widget->style->font->ascent + widget->style->font->descent +
- (widget->allocation.height - widget->requisition.height) / 2);
+ *y = (value_height + (widget->allocation.height - widget->requisition.height) / 2);
break;
case GTK_POS_BOTTOM:
*y = (widget->allocation.height - widget->requisition.height) / 2;
@@ -510,7 +511,6 @@ gtk_hscale_draw_value (GtkScale *scale)
GtkStateType state_type;
GtkWidget *widget;
gchar buffer[32];
- gint text_width;
gint width, height;
gint x, y;
@@ -521,30 +521,30 @@ gtk_hscale_draw_value (GtkScale *scale)
if (scale->draw_value)
{
- sprintf (buffer, "%0.*f", GTK_RANGE (scale)->digits, GTK_RANGE (scale)->adjustment->value);
- text_width = gdk_string_measure (GTK_WIDGET (scale)->style->font, buffer);
+ PangoLayout *layout;
+ PangoRectangle logical_rect;
+ sprintf (buffer, "%0.*f", GTK_RANGE (scale)->digits, GTK_RANGE (scale)->adjustment->value);
+
+ layout = gtk_widget_create_pango_layout (widget);
+ pango_layout_set_text (layout, buffer, -1);
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+
switch (scale->value_pos)
{
case GTK_POS_LEFT:
gdk_window_get_position (GTK_RANGE (scale)->trough, &x, &y);
gdk_window_get_size (GTK_RANGE (scale)->trough, &width, &height);
- x -= SCALE_CLASS (scale)->value_spacing + text_width;
- y += ((height -
- (GTK_WIDGET (scale)->style->font->ascent +
- GTK_WIDGET (scale)->style->font->descent)) / 2 +
- GTK_WIDGET (scale)->style->font->ascent);
+ x -= SCALE_CLASS (scale)->value_spacing + logical_rect.width / PANGO_SCALE;
+ y += (height - logical_rect.height / PANGO_SCALE) / 2;
break;
case GTK_POS_RIGHT:
gdk_window_get_position (GTK_RANGE (scale)->trough, &x, &y);
gdk_window_get_size (GTK_RANGE (scale)->trough, &width, &height);
x += width + SCALE_CLASS (scale)->value_spacing;
- y += ((height -
- (GTK_WIDGET (scale)->style->font->ascent +
- GTK_WIDGET (scale)->style->font->descent)) / 2 +
- GTK_WIDGET (scale)->style->font->ascent);
+ y += (height - logical_rect.height / PANGO_SCALE) / 2;
break;
case GTK_POS_TOP:
gdk_window_get_position (GTK_RANGE (scale)->slider, &x, NULL);
@@ -552,10 +552,10 @@ gtk_hscale_draw_value (GtkScale *scale)
gdk_window_get_size (GTK_RANGE (scale)->slider, &width, NULL);
gdk_window_get_size (GTK_RANGE (scale)->trough, NULL, &height);
- x += widget->allocation.x + (width - text_width) / 2;
+ x += widget->allocation.x + (width - logical_rect.width / PANGO_SCALE) / 2;
x = CLAMP (x, widget->allocation.x,
- widget->allocation.x + widget->allocation.width - text_width);
- y -= GTK_WIDGET (scale)->style->font->descent;
+ widget->allocation.x + widget->allocation.width - logical_rect.width / PANGO_SCALE);
+ y -= logical_rect.height / PANGO_SCALE;
break;
case GTK_POS_BOTTOM:
gdk_window_get_position (GTK_RANGE (scale)->slider, &x, NULL);
@@ -563,22 +563,30 @@ gtk_hscale_draw_value (GtkScale *scale)
gdk_window_get_size (GTK_RANGE (scale)->slider, &width, NULL);
gdk_window_get_size (GTK_RANGE (scale)->trough, NULL, &height);
- x += widget->allocation.x + (width - text_width) / 2;
+ x += widget->allocation.x + (width - logical_rect.width / PANGO_SCALE) / 2;
x = CLAMP (x, widget->allocation.x,
- widget->allocation.x + widget->allocation.width - text_width);
- y += height + GTK_WIDGET (scale)->style->font->ascent;
+ widget->allocation.x + widget->allocation.width - logical_rect.width / PANGO_SCALE);
+ y += height;
break;
}
state_type = GTK_STATE_NORMAL;
if (!GTK_WIDGET_IS_SENSITIVE (scale))
state_type = GTK_STATE_INSENSITIVE;
-
+
+#if 0
gtk_paint_string (GTK_WIDGET (scale)->style,
GTK_WIDGET (scale)->window,
state_type,
NULL, GTK_WIDGET (scale), "hscale",
x, y, buffer);
+#endif
+
+ gdk_draw_layout (GTK_WIDGET (scale)->window,
+ GTK_WIDGET (scale)->style->fg_gc [state_type],
+ x, y, layout);
+
+ pango_layout_unref (layout);
}
}
diff --git a/gtk/gtkhscrollbar.c b/gtk/gtkhscrollbar.c
index e506ff0ae..95eb6eca3 100644
--- a/gtk/gtkhscrollbar.c
+++ b/gtk/gtkhscrollbar.c
@@ -418,7 +418,10 @@ gtk_hscrollbar_calc_slider_size (GtkHScrollbar *hscrollbar)
gdk_window_get_size (range->slider, &slider_width, &slider_height);
if (slider_width != width)
- gdk_window_resize (range->slider, width, slider_height);
+ {
+ gdk_window_resize (range->slider, width, slider_height);
+ gdk_window_invalidate_rect (range->slider, NULL, FALSE);
+ }
}
}
diff --git a/gtk/gtkimcontext.c b/gtk/gtkimcontext.c
new file mode 100644
index 000000000..6f77b098b
--- /dev/null
+++ b/gtk/gtkimcontext.c
@@ -0,0 +1,259 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtkimcontext.h"
+#include "gtksignal.h"
+
+enum {
+ PREEDIT_START,
+ PREEDIT_END,
+ PREEDIT_CHANGED,
+ COMMIT,
+ LAST_SIGNAL
+};
+
+static guint im_context_signals[LAST_SIGNAL] = { 0 };
+
+static void gtk_im_context_class_init (GtkIMContextClass *class);
+static void gtk_im_context_init (GtkIMContext *im_context);
+
+static void gtk_im_context_real_get_preedit_string (GtkIMContext *context,
+ gchar **str,
+ PangoAttrList **attrs);
+static gboolean gtk_im_context_real_filter_keypress (GtkIMContext *context,
+ GdkEventKey *event);
+
+GtkType
+gtk_im_context_get_type (void)
+{
+ static GtkType im_context_type = 0;
+
+ if (!im_context_type)
+ {
+ static const GtkTypeInfo im_context_info =
+ {
+ "GtkIMContext",
+ sizeof (GtkIMContext),
+ sizeof (GtkIMContextClass),
+ (GtkClassInitFunc) gtk_im_context_class_init,
+ (GtkObjectInitFunc) gtk_im_context_init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL,
+ };
+
+ im_context_type = gtk_type_unique (GTK_TYPE_OBJECT, &im_context_info);
+ }
+
+ return im_context_type;
+}
+
+static void
+gtk_im_context_class_init (GtkIMContextClass *klass)
+{
+ GtkObjectClass *object_class;
+
+ object_class = (GtkObjectClass*) klass;
+
+ im_context_signals[PREEDIT_START] =
+ gtk_signal_new ("preedit_start",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkIMContextClass, preedit_start),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ im_context_signals[PREEDIT_END] =
+ gtk_signal_new ("preedit_end",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkIMContextClass, preedit_end),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ im_context_signals[PREEDIT_CHANGED] =
+ gtk_signal_new ("preedit_changed",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkIMContextClass, preedit_changed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ im_context_signals[COMMIT] =
+ gtk_signal_new ("commit",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkIMContextClass, commit),
+ gtk_marshal_NONE__STRING,
+ GTK_TYPE_NONE, 1,
+ GTK_TYPE_STRING);
+
+ klass->get_preedit_string = gtk_im_context_real_get_preedit_string;
+ klass->filter_keypress = gtk_im_context_real_filter_keypress;
+
+ gtk_object_class_add_signals (object_class, im_context_signals, LAST_SIGNAL);
+}
+
+static void
+gtk_im_context_init (GtkIMContext *im_context)
+{
+}
+
+static void
+gtk_im_context_real_get_preedit_string (GtkIMContext *context,
+ gchar **str,
+ PangoAttrList **attrs)
+{
+ if (str)
+ *str = g_strdup ("");
+ if (attrs)
+ *attrs = pango_attr_list_new ();
+}
+
+static gboolean
+gtk_im_context_real_filter_keypress (GtkIMContext *context,
+ GdkEventKey *event)
+{
+ return FALSE;
+}
+
+/**
+ * gtk_im_context_set_client_window:
+ * @context: a #GtkIMContext
+ * @window: the client window. This may be %NULL to indicate
+ * that the previous client window no longer exists.
+ *
+ * Set the client window for the input context; this is the
+ * #GdkWindow in which the input appears. This window is
+ * used in order to correctly position status windows, and may
+ * also be used for purposes internal to the input method.
+ **/
+void
+gtk_im_context_set_client_window (GtkIMContext *context,
+ GdkWindow *window)
+{
+ GtkIMContextClass *klass;
+
+ g_return_if_fail (context != NULL);
+ g_return_if_fail (GTK_IS_IM_CONTEXT (context));
+
+ klass = GTK_IM_CONTEXT_GET_CLASS (context);
+ if (klass->set_client_window)
+ klass->set_client_window (context, window);
+}
+
+/**
+ * gtk_im_context_get_preedit_string:
+ * @context: a #GtkIMContext
+ * @str: location to store the retrieved string. The
+ * string retrieved must be freed with g_free ().
+ * @attrs: location to store the retrieved attribute list.
+ * When you are done with this list, you must
+ * unreference it with pango_attr_list_unref().
+ *
+ * Retrieve the current preedit string for the input context,
+ * and a list of attributes to apply to the string.
+ * This string should be displayed inserted at the insertion
+ * point.
+ **/
+void
+gtk_im_context_get_preedit_string (GtkIMContext *context,
+ gchar **str,
+ PangoAttrList **attrs)
+{
+ GtkIMContextClass *klass;
+
+ g_return_if_fail (context != NULL);
+ g_return_if_fail (GTK_IS_IM_CONTEXT (context));
+
+ klass = GTK_IM_CONTEXT_GET_CLASS (context);
+ klass->get_preedit_string (context, str, attrs);
+}
+
+/**
+ * gtk_im_context_filter_keypress:
+ * @context: a #GtkIMContext
+ * @key: the key event
+ *
+ * Allow an input method to internally handle a key press event.
+ * if this function returns %TRUE, then no further processing
+ * should be done for this keystroke.
+ *
+ * Return value: %TRUE if the input method handled the keystroke.
+ *
+ **/
+gboolean
+gtk_im_context_filter_keypress (GtkIMContext *context,
+ GdkEventKey *key)
+{
+ GtkIMContextClass *klass;
+
+ g_return_val_if_fail (context != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_IM_CONTEXT (context), FALSE);
+ g_return_val_if_fail (key != NULL, FALSE);
+
+ klass = GTK_IM_CONTEXT_GET_CLASS (context);
+ return klass->filter_keypress (context, key);
+}
+
+/**
+ * gtk_im_context_focus_in:
+ * @context: a #GtkIMContext
+ *
+ * Notify the input method that the widget to which this
+ * input context corresponds has lost gained. The input method
+ * may, for example, change the displayed feedback to reflect
+ * this change.
+ **/
+void
+gtk_im_context_focus_in (GtkIMContext *context)
+{
+ GtkIMContextClass *klass;
+
+ g_return_if_fail (context != NULL);
+ g_return_if_fail (GTK_IS_IM_CONTEXT (context));
+
+ klass = GTK_IM_CONTEXT_GET_CLASS (context);
+ if (klass->focus_in)
+ klass->focus_in (context);
+}
+
+/**
+ * gtk_im_context_focus_in:
+ * @context: a #GtkIMContext
+ *
+ * Notify the input method that the widget to which this
+ * input context corresponds has lost focus. The input method
+ * may, for example, change the displayed feedback or reset the contexts
+ * state to reflect this change.
+ **/
+void
+gtk_im_context_focus_out (GtkIMContext *context)
+{
+ GtkIMContextClass *klass;
+
+ g_return_if_fail (context != NULL);
+ g_return_if_fail (GTK_IS_IM_CONTEXT (context));
+
+ klass = GTK_IM_CONTEXT_GET_CLASS (context);
+ if (klass->focus_out)
+ klass->focus_out (context);
+}
+
+
diff --git a/gtk/gtkimcontext.h b/gtk/gtkimcontext.h
new file mode 100644
index 000000000..27dc0761d
--- /dev/null
+++ b/gtk/gtkimcontext.h
@@ -0,0 +1,87 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2000 Red Hat Software
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_IM_CONTEXT_H__
+#define __GTK_IM_CONTEXT_H__
+
+#include <gdk/gdk.h>
+#include <gtk/gtkobject.h>
+#include <pango/pango.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#define GTK_TYPE_IM_CONTEXT (gtk_im_context_get_type ())
+#define GTK_IM_CONTEXT(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_IM_CONTEXT, GtkIMContext))
+#define GTK_IM_CONTEXT_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_IM_CONTEXT, GtkIMContextClass))
+#define GTK_IS_IM_CONTEXT(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_IM_CONTEXT))
+#define GTK_IS_IM_CONTEXT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_IM_CONTEXT))
+#define GTK_IM_CONTEXT_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_IM_CONTEXT, GtkIMContextClass))
+
+
+typedef struct _GtkIMContext GtkIMContext;
+typedef struct _GtkIMContextClass GtkIMContextClass;
+
+struct _GtkIMContext
+{
+ GtkObject object;
+};
+
+struct _GtkIMContextClass
+{
+ GtkObjectClass parent_class;
+
+ /* Signals */
+ void (*preedit_start) (GtkIMContext *context);
+ void (*preedit_end) (GtkIMContext *context);
+ void (*preedit_changed) (GtkIMContext *context);
+ void (*commit) (GtkIMContext *context, const gchar *str);
+
+ /* Virtual functions */
+ void (*set_client_window) (GtkIMContext *context,
+ GdkWindow *window);
+ void (*get_preedit_string) (GtkIMContext *context,
+ gchar **str,
+ PangoAttrList **attrs);
+ gboolean (*filter_keypress) (GtkIMContext *context,
+ GdkEventKey *event);
+ void (*focus_in) (GtkIMContext *context);
+ void (*focus_out) (GtkIMContext *context);
+};
+
+GtkType gtk_im_context_get_type (void);
+
+void gtk_im_context_set_client_window (GtkIMContext *context,
+ GdkWindow *window);
+void gtk_im_context_get_preedit_string (GtkIMContext *context,
+ char **str,
+ PangoAttrList **attrs);
+gboolean gtk_im_context_filter_keypress (GtkIMContext *context,
+ GdkEventKey *event);
+void gtk_im_context_focus_in (GtkIMContext *context);
+void gtk_im_context_focus_out (GtkIMContext *context);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_IM_CONTEXT_H__ */
diff --git a/gtk/gtkimcontextsimple.c b/gtk/gtkimcontextsimple.c
new file mode 100644
index 000000000..03ddcc331
--- /dev/null
+++ b/gtk/gtkimcontextsimple.c
@@ -0,0 +1,932 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <unicode.h>
+#include <gdk/gdkkeysyms.h>
+#include "gtksignal.h"
+#include "gtkimcontextsimple.h"
+
+typedef struct _GtkComposeSeq GtkComposeSeq;
+
+struct _GtkComposeSeq
+{
+ guint16 keysyms[GTK_MAX_COMPOSE_LEN];
+ guint16 unicode;
+};
+
+/* The following table was generated from the X compose tables include with
+ * XFree86 4.0 using a set of Perl scripts. Contact Owen Taylor <otaylor@redhat.com>
+ * to obtain the relevant perl scripts.
+ *
+ * The following compose letter letter sequences confliced
+ * Dstroke/dstroke and ETH/eth; resolved to Dstroke (Croation, Vietnamese, Lappish), over
+ * ETH (Icelandic, Faroese, old English, IPA) [ D- -D d- -d ]
+ * Amacron/amacron and ordfeminine; resolved to ordfeminine [ _A A_ a_ _a ]
+ * Amacron/amacron and Atilde/atilde; resolved to atilde [ -A A- a- -a ]
+ * Omacron/Omacron and masculine; resolved to masculine [ _A A_ a_ _a ]
+ * Omacron/omacron and Otilde/atilde; resolved to otilde [ -O O- o- -o ]
+ *
+ * [ Amacron and Omacron are in Latin-4 (Baltic). ordfeminine and masculine are used for
+ * spanish. atilde and otilde are used at least for Portuguese ]
+ *
+ * at and Aring; resolved to Aring [ AA ]
+ * guillemotleft and caron; resolved to guillemotleft [ << ]
+ * ogonek and cedilla; resolved to cedilla [ ,, ]
+ *
+ * This probably should be resolved by first checking an additional set of compose tables
+ * that depend on the locale or selected input method.
+ */
+
+GtkComposeSeq gtk_compose_seqs[] = {
+ { { GDK_dead_grave, GDK_space, 0, 0 }, 0x0060 }, /* GRAVE_ACCENT */
+ { { GDK_dead_grave, GDK_A, 0, 0 }, 0x00C0 }, /* LATIN_CAPITAL_LETTER_A_WITH_GRAVE */
+ { { GDK_dead_grave, GDK_E, 0, 0 }, 0x00C8 }, /* LATIN_CAPITAL_LETTER_E_WITH_GRAVE */
+ { { GDK_dead_grave, GDK_I, 0, 0 }, 0x00CC }, /* LATIN_CAPITAL_LETTER_I_WITH_GRAVE */
+ { { GDK_dead_grave, GDK_O, 0, 0 }, 0x00D2 }, /* LATIN_CAPITAL_LETTER_O_WITH_GRAVE */
+ { { GDK_dead_grave, GDK_U, 0, 0 }, 0x00D9 }, /* LATIN_CAPITAL_LETTER_U_WITH_GRAVE */
+ { { GDK_dead_grave, GDK_a, 0, 0 }, 0x00E0 }, /* LATIN_SMALL_LETTER_A_WITH_GRAVE */
+ { { GDK_dead_grave, GDK_e, 0, 0 }, 0x00E8 }, /* LATIN_SMALL_LETTER_E_WITH_GRAVE */
+ { { GDK_dead_grave, GDK_i, 0, 0 }, 0x00EC }, /* LATIN_SMALL_LETTER_I_WITH_GRAVE */
+ { { GDK_dead_grave, GDK_o, 0, 0 }, 0x00F2 }, /* LATIN_SMALL_LETTER_O_WITH_GRAVE */
+ { { GDK_dead_grave, GDK_u, 0, 0 }, 0x00F9 }, /* LATIN_SMALL_LETTER_U_WITH_GRAVE */
+ { { GDK_dead_acute, GDK_space, 0, 0 }, 0x0027 }, /* APOSTROPHE */
+ { { GDK_dead_acute, GDK_apostrophe, 0, 0 }, 0x00B4 }, /* ACUTE_ACCENT */
+ { { GDK_dead_acute, GDK_A, 0, 0 }, 0x00C1 }, /* LATIN_CAPITAL_LETTER_A_WITH_ACUTE */
+ { { GDK_dead_acute, GDK_E, 0, 0 }, 0x00C9 }, /* LATIN_CAPITAL_LETTER_E_WITH_ACUTE */
+ { { GDK_dead_acute, GDK_I, 0, 0 }, 0x00CD }, /* LATIN_CAPITAL_LETTER_I_WITH_ACUTE */
+ { { GDK_dead_acute, GDK_O, 0, 0 }, 0x00D3 }, /* LATIN_CAPITAL_LETTER_O_WITH_ACUTE */
+ { { GDK_dead_acute, GDK_U, 0, 0 }, 0x00DA }, /* LATIN_CAPITAL_LETTER_U_WITH_ACUTE */
+ { { GDK_dead_acute, GDK_Y, 0, 0 }, 0x00DD }, /* LATIN_CAPITAL_LETTER_Y_WITH_ACUTE */
+ { { GDK_dead_acute, GDK_a, 0, 0 }, 0x00E1 }, /* LATIN_SMALL_LETTER_A_WITH_ACUTE */
+ { { GDK_dead_acute, GDK_e, 0, 0 }, 0x00E9 }, /* LATIN_SMALL_LETTER_E_WITH_ACUTE */
+ { { GDK_dead_acute, GDK_i, 0, 0 }, 0x00ED }, /* LATIN_SMALL_LETTER_I_WITH_ACUTE */
+ { { GDK_dead_acute, GDK_o, 0, 0 }, 0x00F3 }, /* LATIN_SMALL_LETTER_O_WITH_ACUTE */
+ { { GDK_dead_acute, GDK_u, 0, 0 }, 0x00FA }, /* LATIN_SMALL_LETTER_U_WITH_ACUTE */
+ { { GDK_dead_acute, GDK_y, 0, 0 }, 0x00FD }, /* LATIN_SMALL_LETTER_Y_WITH_ACUTE */
+ { { GDK_dead_acute, GDK_acute, 0, 0 }, 0x00B4 }, /* ACUTE_ACCENT */
+ { { GDK_dead_acute, GDK_dead_acute, 0, 0 }, 0x00B4 }, /* ACUTE_ACCENT */
+ { { GDK_dead_circumflex, GDK_space, 0, 0 }, 0x005E }, /* CIRCUMFLEX_ACCENT */
+ { { GDK_dead_circumflex, GDK_minus, 0, 0 }, 0x00AF }, /* MACRON */
+ { { GDK_dead_circumflex, GDK_period, 0, 0 }, 0x00B7 }, /* MIDDLE_DOT */
+ { { GDK_dead_circumflex, GDK_slash, 0, 0 }, 0x007C }, /* VERTICAL_LINE */
+ { { GDK_dead_circumflex, GDK_0, 0, 0 }, 0x00B0 }, /* DEGREE_SIGN */
+ { { GDK_dead_circumflex, GDK_1, 0, 0 }, 0x00B9 }, /* SUPERSCRIPT_ONE */
+ { { GDK_dead_circumflex, GDK_2, 0, 0 }, 0x00B2 }, /* SUPERSCRIPT_TWO */
+ { { GDK_dead_circumflex, GDK_3, 0, 0 }, 0x00B3 }, /* SUPERSCRIPT_THREE */
+ { { GDK_dead_circumflex, GDK_A, 0, 0 }, 0x00C2 }, /* LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX */
+ { { GDK_dead_circumflex, GDK_E, 0, 0 }, 0x00CA }, /* LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX */
+ { { GDK_dead_circumflex, GDK_I, 0, 0 }, 0x00CE }, /* LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX */
+ { { GDK_dead_circumflex, GDK_O, 0, 0 }, 0x00D4 }, /* LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX */
+ { { GDK_dead_circumflex, GDK_U, 0, 0 }, 0x00DB }, /* LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX */
+ { { GDK_dead_circumflex, GDK_underscore, 0, 0 }, 0x00AF }, /* MACRON */
+ { { GDK_dead_circumflex, GDK_a, 0, 0 }, 0x00E2 }, /* LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX */
+ { { GDK_dead_circumflex, GDK_e, 0, 0 }, 0x00EA }, /* LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX */
+ { { GDK_dead_circumflex, GDK_i, 0, 0 }, 0x00EE }, /* LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX */
+ { { GDK_dead_circumflex, GDK_o, 0, 0 }, 0x00F4 }, /* LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX */
+ { { GDK_dead_circumflex, GDK_u, 0, 0 }, 0x00FB }, /* LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX */
+ { { GDK_dead_tilde, GDK_space, 0, 0 }, 0x007E }, /* TILDE */
+ { { GDK_dead_tilde, GDK_A, 0, 0 }, 0x00C3 }, /* LATIN_CAPITAL_LETTER_A_WITH_TILDE */
+ { { GDK_dead_tilde, GDK_I, 0, 0 }, 0x0128 }, /* LATIN_CAPITAL_LETTER_I_WITH_TILDE */
+ { { GDK_dead_tilde, GDK_N, 0, 0 }, 0x00D1 }, /* LATIN_CAPITAL_LETTER_N_WITH_TILDE */
+ { { GDK_dead_tilde, GDK_O, 0, 0 }, 0x00D5 }, /* LATIN_CAPITAL_LETTER_O_WITH_TILDE */
+ { { GDK_dead_tilde, GDK_U, 0, 0 }, 0x0168 }, /* LATIN_CAPITAL_LETTER_U_WITH_TILDE */
+ { { GDK_dead_tilde, GDK_a, 0, 0 }, 0x00E3 }, /* LATIN_SMALL_LETTER_A_WITH_TILDE */
+ { { GDK_dead_tilde, GDK_i, 0, 0 }, 0x0129 }, /* LATIN_SMALL_LETTER_I_WITH_TILDE */
+ { { GDK_dead_tilde, GDK_n, 0, 0 }, 0x00F1 }, /* LATIN_SMALL_LETTER_N_WITH_TILDE */
+ { { GDK_dead_tilde, GDK_o, 0, 0 }, 0x00F5 }, /* LATIN_SMALL_LETTER_O_WITH_TILDE */
+ { { GDK_dead_tilde, GDK_u, 0, 0 }, 0x0169 }, /* LATIN_SMALL_LETTER_U_WITH_TILDE */
+ { { GDK_dead_tilde, GDK_asciitilde, 0, 0 }, 0x007E }, /* TILDE */
+ { { GDK_dead_tilde, GDK_dead_tilde, 0, 0 }, 0x007E }, /* TILDE */
+ { { GDK_dead_macron, GDK_A, 0, 0 }, 0x0100 }, /* LATIN_CAPITAL_LETTER_A_WITH_MACRON */
+ { { GDK_dead_macron, GDK_E, 0, 0 }, 0x0112 }, /* LATIN_CAPITAL_LETTER_E_WITH_MACRON */
+ { { GDK_dead_macron, GDK_I, 0, 0 }, 0x012A }, /* LATIN_CAPITAL_LETTER_I_WITH_MACRON */
+ { { GDK_dead_macron, GDK_O, 0, 0 }, 0x014C }, /* LATIN_CAPITAL_LETTER_O_WITH_MACRON */
+ { { GDK_dead_macron, GDK_U, 0, 0 }, 0x00D9 }, /* LATIN_CAPITAL_LETTER_U_WITH_GRAVE */
+ { { GDK_dead_macron, GDK_a, 0, 0 }, 0x0101 }, /* LATIN_SMALL_LETTER_A_WITH_MACRON */
+ { { GDK_dead_macron, GDK_e, 0, 0 }, 0x0113 }, /* LATIN_SMALL_LETTER_E_WITH_MACRON */
+ { { GDK_dead_macron, GDK_i, 0, 0 }, 0x012B }, /* LATIN_SMALL_LETTER_I_WITH_MACRON */
+ { { GDK_dead_macron, GDK_o, 0, 0 }, 0x014D }, /* LATIN_SMALL_LETTER_O_WITH_MACRON */
+ { { GDK_dead_macron, GDK_u, 0, 0 }, 0x016B }, /* LATIN_SMALL_LETTER_U_WITH_MACRON */
+ { { GDK_dead_macron, GDK_macron, 0, 0 }, 0x00AF }, /* MACRON */
+ { { GDK_dead_macron, GDK_dead_macron, 0, 0 }, 0x00AF }, /* MACRON */
+ { { GDK_dead_breve, GDK_G, 0, 0 }, 0x011E }, /* LATIN_CAPITAL_LETTER_G_WITH_BREVE */
+ { { GDK_dead_breve, GDK_g, 0, 0 }, 0x011F }, /* LATIN_SMALL_LETTER_G_WITH_BREVE */
+ { { GDK_dead_abovedot, GDK_E, 0, 0 }, 0x0116 }, /* LATIN_CAPITAL_LETTER_E_WITH_DOT_ABOVE */
+ { { GDK_dead_abovedot, GDK_I, 0, 0 }, 0x0130 }, /* LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE */
+ { { GDK_dead_abovedot, GDK_e, 0, 0 }, 0x0117 }, /* LATIN_SMALL_LETTER_E_WITH_DOT_ABOVE */
+ { { GDK_dead_abovedot, GDK_i, 0, 0 }, 0x0131 }, /* LATIN_SMALL_LETTER_DOTLESS_I */
+ { { GDK_dead_abovedot, GDK_abovedot, 0, 0 }, 0x02D9 }, /* DOT_ABOVE */
+ { { GDK_dead_abovedot, GDK_dead_abovedot, 0, 0 }, 0x02D9 }, /* DOT_ABOVE */
+ { { GDK_dead_diaeresis, GDK_space, 0, 0 }, 0x00A8 }, /* DIAERESIS */
+ { { GDK_dead_diaeresis, GDK_quotedbl, 0, 0 }, 0x00A8 }, /* DIAERESIS */
+ { { GDK_dead_diaeresis, GDK_A, 0, 0 }, 0x00C4 }, /* LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS */
+ { { GDK_dead_diaeresis, GDK_E, 0, 0 }, 0x00CB }, /* LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS */
+ { { GDK_dead_diaeresis, GDK_I, 0, 0 }, 0x00CF }, /* LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS */
+ { { GDK_dead_diaeresis, GDK_O, 0, 0 }, 0x00D6 }, /* LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS */
+ { { GDK_dead_diaeresis, GDK_U, 0, 0 }, 0x00DC }, /* LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS */
+ { { GDK_dead_diaeresis, GDK_Y, 0, 0 }, 0x0178 }, /* LATIN_CAPITAL_LETTER_Y_WITH_DIAERESIS */
+ { { GDK_dead_diaeresis, GDK_a, 0, 0 }, 0x00E4 }, /* LATIN_SMALL_LETTER_A_WITH_DIAERESIS */
+ { { GDK_dead_diaeresis, GDK_e, 0, 0 }, 0x00EB }, /* LATIN_SMALL_LETTER_E_WITH_DIAERESIS */
+ { { GDK_dead_diaeresis, GDK_i, 0, 0 }, 0x00EF }, /* LATIN_SMALL_LETTER_I_WITH_DIAERESIS */
+ { { GDK_dead_diaeresis, GDK_o, 0, 0 }, 0x00F6 }, /* LATIN_SMALL_LETTER_O_WITH_DIAERESIS */
+ { { GDK_dead_diaeresis, GDK_u, 0, 0 }, 0x00FC }, /* LATIN_SMALL_LETTER_U_WITH_DIAERESIS */
+ { { GDK_dead_diaeresis, GDK_y, 0, 0 }, 0x00FF }, /* LATIN_SMALL_LETTER_Y_WITH_DIAERESIS */
+ { { GDK_dead_diaeresis, GDK_diaeresis, 0, 0 }, 0x00A8 }, /* DIAERESIS */
+ { { GDK_dead_diaeresis, GDK_dead_diaeresis, 0, 0 }, 0x00A8 }, /* DIAERESIS */
+ { { GDK_dead_abovering, GDK_A, 0, 0 }, 0x00C5 }, /* LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE */
+ { { GDK_dead_abovering, GDK_a, 0, 0 }, 0x00E5 }, /* LATIN_SMALL_LETTER_A_WITH_RING_ABOVE */
+ { { GDK_dead_abovering, GDK_dead_abovering, 0, 0 }, 0x02DA }, /* RING_ABOVE */
+ { { GDK_dead_caron, GDK_C, 0, 0 }, 0x010C }, /* LATIN_CAPITAL_LETTER_C_WITH_CARON */
+ { { GDK_dead_caron, GDK_S, 0, 0 }, 0x0160 }, /* LATIN_CAPITAL_LETTER_S_WITH_CARON */
+ { { GDK_dead_caron, GDK_Z, 0, 0 }, 0x017D }, /* LATIN_CAPITAL_LETTER_Z_WITH_CARON */
+ { { GDK_dead_caron, GDK_c, 0, 0 }, 0x010D }, /* LATIN_SMALL_LETTER_C_WITH_CARON */
+ { { GDK_dead_caron, GDK_s, 0, 0 }, 0x0161 }, /* LATIN_SMALL_LETTER_S_WITH_CARON */
+ { { GDK_dead_caron, GDK_z, 0, 0 }, 0x017E }, /* LATIN_SMALL_LETTER_Z_WITH_CARON */
+ { { GDK_dead_caron, GDK_caron, 0, 0 }, 0x02C7 }, /* CARON */
+ { { GDK_dead_caron, GDK_dead_caron, 0, 0 }, 0x02C7 }, /* CARON */
+ { { GDK_dead_cedilla, GDK_comma, 0, 0 }, 0x00B8 }, /* CEDILLA */
+ { { GDK_dead_cedilla, GDK_minus, 0, 0 }, 0x00AC }, /* NOT_SIGN */
+ { { GDK_dead_cedilla, GDK_C, 0, 0 }, 0x00C7 }, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */
+ { { GDK_dead_cedilla, GDK_G, 0, 0 }, 0x0122 }, /* LATIN_CAPITAL_LETTER_G_WITH_CEDILLA */
+ { { GDK_dead_cedilla, GDK_K, 0, 0 }, 0x0136 }, /* LATIN_CAPITAL_LETTER_K_WITH_CEDILLA */
+ { { GDK_dead_cedilla, GDK_L, 0, 0 }, 0x013B }, /* LATIN_CAPITAL_LETTER_L_WITH_CEDILLA */
+ { { GDK_dead_cedilla, GDK_N, 0, 0 }, 0x0145 }, /* LATIN_CAPITAL_LETTER_N_WITH_CEDILLA */
+ { { GDK_dead_cedilla, GDK_R, 0, 0 }, 0x0156 }, /* LATIN_CAPITAL_LETTER_R_WITH_CEDILLA */
+ { { GDK_dead_cedilla, GDK_S, 0, 0 }, 0x015E }, /* LATIN_CAPITAL_LETTER_S_WITH_CEDILLA */
+ { { GDK_dead_cedilla, GDK_c, 0, 0 }, 0x00E7 }, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */
+ { { GDK_dead_cedilla, GDK_g, 0, 0 }, 0x0123 }, /* LATIN_SMALL_LETTER_G_WITH_CEDILLA */
+ { { GDK_dead_cedilla, GDK_k, 0, 0 }, 0x0137 }, /* LATIN_SMALL_LETTER_K_WITH_CEDILLA */
+ { { GDK_dead_cedilla, GDK_l, 0, 0 }, 0x013C }, /* LATIN_SMALL_LETTER_L_WITH_CEDILLA */
+ { { GDK_dead_cedilla, GDK_n, 0, 0 }, 0x0146 }, /* LATIN_SMALL_LETTER_N_WITH_CEDILLA */
+ { { GDK_dead_cedilla, GDK_r, 0, 0 }, 0x0157 }, /* LATIN_SMALL_LETTER_R_WITH_CEDILLA */
+ { { GDK_dead_cedilla, GDK_s, 0, 0 }, 0x015F }, /* LATIN_SMALL_LETTER_S_WITH_CEDILLA */
+ { { GDK_dead_cedilla, GDK_cedilla, 0, 0 }, 0x00B8 }, /* CEDILLA */
+ { { GDK_dead_cedilla, GDK_dead_cedilla, 0, 0 }, 0x00B8 }, /* CEDILLA */
+ { { GDK_dead_ogonek, GDK_A, 0, 0 }, 0x0104 }, /* LATIN_CAPITAL_LETTER_A_WITH_OGONEK */
+ { { GDK_dead_ogonek, GDK_E, 0, 0 }, 0x0118 }, /* LATIN_CAPITAL_LETTER_E_WITH_OGONEK */
+ { { GDK_dead_ogonek, GDK_I, 0, 0 }, 0x012E }, /* LATIN_CAPITAL_LETTER_I_WITH_OGONEK */
+ { { GDK_dead_ogonek, GDK_U, 0, 0 }, 0x0172 }, /* LATIN_CAPITAL_LETTER_U_WITH_OGONEK */
+ { { GDK_dead_ogonek, GDK_a, 0, 0 }, 0x0105 }, /* LATIN_SMALL_LETTER_A_WITH_OGONEK */
+ { { GDK_dead_ogonek, GDK_e, 0, 0 }, 0x0119 }, /* LATIN_SMALL_LETTER_E_WITH_OGONEK */
+ { { GDK_dead_ogonek, GDK_i, 0, 0 }, 0x012F }, /* LATIN_SMALL_LETTER_I_WITH_OGONEK */
+ { { GDK_dead_ogonek, GDK_u, 0, 0 }, 0x0173 }, /* LATIN_SMALL_LETTER_U_WITH_OGONEK */
+ { { GDK_dead_ogonek, GDK_ogonek, 0, 0 }, 0x02DB }, /* OGONEK */
+ { { GDK_dead_ogonek, GDK_dead_ogonek, 0, 0 }, 0x02DB }, /* OGONEK */
+ { { GDK_Multi_key, GDK_space, GDK_space, 0 }, 0x00A0 }, /* NOxBREAK_SPACE */
+ { { GDK_Multi_key, GDK_space, GDK_apostrophe, 0 }, 0x0027 }, /* APOSTROPHE */
+ { { GDK_Multi_key, GDK_space, GDK_minus, 0 }, 0x007E }, /* TILDE */
+ { { GDK_Multi_key, GDK_space, GDK_greater, 0 }, 0x005E }, /* CIRCUMFLEX_ACCENT */
+ { { GDK_Multi_key, GDK_space, GDK_asciicircum, 0 }, 0x005E }, /* CIRCUMFLEX_ACCENT */
+ { { GDK_Multi_key, GDK_space, GDK_grave, 0 }, 0x0060 }, /* GRAVE_ACCENT */
+ { { GDK_Multi_key, GDK_space, GDK_asciitilde, 0 }, 0x007E }, /* TILDE */
+ { { GDK_Multi_key, GDK_exclam, GDK_exclam, 0 }, 0x00A1 }, /* INVERTED_EXCLAMATION_MARK */
+ { { GDK_Multi_key, GDK_exclam, GDK_P, 0 }, 0x00B6 }, /* PILCROW_SIGN */
+ { { GDK_Multi_key, GDK_exclam, GDK_S, 0 }, 0x00A7 }, /* SECTION_SIGN */
+ { { GDK_Multi_key, GDK_exclam, GDK_p, 0 }, 0x00B6 }, /* PILCROW_SIGN */
+ { { GDK_Multi_key, GDK_exclam, GDK_s, 0 }, 0x00A7 }, /* SECTION_SIGN */
+ { { GDK_Multi_key, GDK_quotedbl, GDK_quotedbl, 0 }, 0x00A8 }, /* DIAERESIS */
+ { { GDK_Multi_key, GDK_quotedbl, GDK_A, 0 }, 0x00C4 }, /* LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_quotedbl, GDK_E, 0 }, 0x00CB }, /* LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_quotedbl, GDK_I, 0 }, 0x00CF }, /* LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_quotedbl, GDK_O, 0 }, 0x00D6 }, /* LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_quotedbl, GDK_U, 0 }, 0x00DC }, /* LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_quotedbl, GDK_Y, 0 }, 0x0178 }, /* LATIN_CAPITAL_LETTER_Y_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_quotedbl, GDK_a, 0 }, 0x00E4 }, /* LATIN_SMALL_LETTER_A_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_quotedbl, GDK_e, 0 }, 0x00EB }, /* LATIN_SMALL_LETTER_E_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_quotedbl, GDK_i, 0 }, 0x00EF }, /* LATIN_SMALL_LETTER_I_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_quotedbl, GDK_o, 0 }, 0x00F6 }, /* LATIN_SMALL_LETTER_O_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_quotedbl, GDK_u, 0 }, 0x00FC }, /* LATIN_SMALL_LETTER_U_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_quotedbl, GDK_y, 0 }, 0x00FF }, /* LATIN_SMALL_LETTER_Y_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_apostrophe, GDK_space, 0 }, 0x0027 }, /* APOSTROPHE */
+ { { GDK_Multi_key, GDK_apostrophe, GDK_apostrophe, 0 }, 0x00B4 }, /* ACUTE_ACCENT */
+ { { GDK_Multi_key, GDK_apostrophe, GDK_A, 0 }, 0x00C1 }, /* LATIN_CAPITAL_LETTER_A_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_apostrophe, GDK_E, 0 }, 0x00C9 }, /* LATIN_CAPITAL_LETTER_E_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_apostrophe, GDK_I, 0 }, 0x00CD }, /* LATIN_CAPITAL_LETTER_I_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_apostrophe, GDK_O, 0 }, 0x00D3 }, /* LATIN_CAPITAL_LETTER_O_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_apostrophe, GDK_U, 0 }, 0x00DA }, /* LATIN_CAPITAL_LETTER_U_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_apostrophe, GDK_Y, 0 }, 0x00DD }, /* LATIN_CAPITAL_LETTER_Y_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_apostrophe, GDK_a, 0 }, 0x00E1 }, /* LATIN_SMALL_LETTER_A_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_apostrophe, GDK_e, 0 }, 0x00E9 }, /* LATIN_SMALL_LETTER_E_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_apostrophe, GDK_i, 0 }, 0x00ED }, /* LATIN_SMALL_LETTER_I_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_apostrophe, GDK_o, 0 }, 0x00F3 }, /* LATIN_SMALL_LETTER_O_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_apostrophe, GDK_u, 0 }, 0x00FA }, /* LATIN_SMALL_LETTER_U_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_apostrophe, GDK_y, 0 }, 0x00FD }, /* LATIN_SMALL_LETTER_Y_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_parenleft, GDK_parenleft, 0 }, 0x005B }, /* LEFT_SQUARE_BRACKET */
+ { { GDK_Multi_key, GDK_parenleft, GDK_minus, 0 }, 0x007B }, /* LEFT_CURLY_BRACKET */
+ { { GDK_Multi_key, GDK_parenleft, GDK_G, 0 }, 0x011E }, /* LATIN_CAPITAL_LETTER_G_WITH_BREVE */
+ { { GDK_Multi_key, GDK_parenleft, GDK_c, 0 }, 0x00A9 }, /* COPYRIGHT_SIGN */
+ { { GDK_Multi_key, GDK_parenleft, GDK_g, 0 }, 0x011F }, /* LATIN_SMALL_LETTER_G_WITH_BREVE */
+ { { GDK_Multi_key, GDK_parenleft, GDK_r, 0 }, 0x00AE }, /* REGISTERED_SIGN */
+ { { GDK_Multi_key, GDK_parenright, GDK_parenright, 0 }, 0x005D }, /* RIGHT_SQUARE_BRACKET */
+ { { GDK_Multi_key, GDK_parenright, GDK_minus, 0 }, 0x007D }, /* RIGHT_CURLY_BRACKET */
+ { { GDK_Multi_key, GDK_asterisk, GDK_0, 0 }, 0x00B0 }, /* DEGREE_SIGN */
+ { { GDK_Multi_key, GDK_asterisk, GDK_A, 0 }, 0x00C5 }, /* LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE */
+ { { GDK_Multi_key, GDK_asterisk, GDK_a, 0 }, 0x00E5 }, /* LATIN_SMALL_LETTER_A_WITH_RING_ABOVE */
+ { { GDK_Multi_key, GDK_plus, GDK_plus, 0 }, 0x0023 }, /* NUMBER_SIGN */
+ { { GDK_Multi_key, GDK_plus, GDK_minus, 0 }, 0x00B1 }, /* PLUSxMINUS_SIGN */
+ { { GDK_Multi_key, GDK_comma, GDK_comma, 0 }, 0x00B8 }, /* CEDILLA */
+ { { GDK_Multi_key, GDK_comma, GDK_minus, 0 }, 0x00AC }, /* NOT_SIGN */
+ { { GDK_Multi_key, GDK_comma, GDK_A, 0 }, 0x0104 }, /* LATIN_CAPITAL_LETTER_A_WITH_OGONEK */
+ { { GDK_Multi_key, GDK_comma, GDK_C, 0 }, 0x00C7 }, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_comma, GDK_E, 0 }, 0x0118 }, /* LATIN_CAPITAL_LETTER_E_WITH_OGONEK */
+ { { GDK_Multi_key, GDK_comma, GDK_G, 0 }, 0x0122 }, /* LATIN_CAPITAL_LETTER_G_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_comma, GDK_I, 0 }, 0x012E }, /* LATIN_CAPITAL_LETTER_I_WITH_OGONEK */
+ { { GDK_Multi_key, GDK_comma, GDK_K, 0 }, 0x0136 }, /* LATIN_CAPITAL_LETTER_K_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_comma, GDK_L, 0 }, 0x013B }, /* LATIN_CAPITAL_LETTER_L_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_comma, GDK_N, 0 }, 0x0145 }, /* LATIN_CAPITAL_LETTER_N_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_comma, GDK_R, 0 }, 0x0156 }, /* LATIN_CAPITAL_LETTER_R_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_comma, GDK_S, 0 }, 0x015E }, /* LATIN_CAPITAL_LETTER_S_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_comma, GDK_U, 0 }, 0x0172 }, /* LATIN_CAPITAL_LETTER_U_WITH_OGONEK */
+ { { GDK_Multi_key, GDK_comma, GDK_a, 0 }, 0x0105 }, /* LATIN_SMALL_LETTER_A_WITH_OGONEK */
+ { { GDK_Multi_key, GDK_comma, GDK_c, 0 }, 0x00E7 }, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_comma, GDK_e, 0 }, 0x0119 }, /* LATIN_SMALL_LETTER_E_WITH_OGONEK */
+ { { GDK_Multi_key, GDK_comma, GDK_g, 0 }, 0x0123 }, /* LATIN_SMALL_LETTER_G_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_comma, GDK_i, 0 }, 0x012F }, /* LATIN_SMALL_LETTER_I_WITH_OGONEK */
+ { { GDK_Multi_key, GDK_comma, GDK_k, 0 }, 0x0137 }, /* LATIN_SMALL_LETTER_K_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_comma, GDK_l, 0 }, 0x013C }, /* LATIN_SMALL_LETTER_L_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_comma, GDK_n, 0 }, 0x0146 }, /* LATIN_SMALL_LETTER_N_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_comma, GDK_r, 0 }, 0x0157 }, /* LATIN_SMALL_LETTER_R_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_comma, GDK_s, 0 }, 0x015F }, /* LATIN_SMALL_LETTER_S_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_comma, GDK_u, 0 }, 0x0173 }, /* LATIN_SMALL_LETTER_U_WITH_OGONEK */
+ { { GDK_Multi_key, GDK_minus, GDK_space, 0 }, 0x007E }, /* TILDE */
+ { { GDK_Multi_key, GDK_minus, GDK_parenleft, 0 }, 0x007B }, /* LEFT_CURLY_BRACKET */
+ { { GDK_Multi_key, GDK_minus, GDK_parenright, 0 }, 0x007D }, /* RIGHT_CURLY_BRACKET */
+ { { GDK_Multi_key, GDK_minus, GDK_plus, 0 }, 0x00B1 }, /* PLUSxMINUS_SIGN */
+ { { GDK_Multi_key, GDK_minus, GDK_comma, 0 }, 0x00AC }, /* NOT_SIGN */
+ { { GDK_Multi_key, GDK_minus, GDK_minus, 0 }, 0x00AD }, /* SOFT_HYPHEN */
+ { { GDK_Multi_key, GDK_minus, GDK_colon, 0 }, 0x00F7 }, /* DIVISION_SIGN */
+ { { GDK_Multi_key, GDK_minus, GDK_A, 0 }, 0x00C3 }, /* LATIN_CAPITAL_LETTER_A_WITH_TILDE */
+ { { GDK_Multi_key, GDK_minus, GDK_D, 0 }, 0x0110 }, /* LATIN_CAPITAL_LETTER_D_WITH_STROKE */
+ { { GDK_Multi_key, GDK_minus, GDK_E, 0 }, 0x0112 }, /* LATIN_CAPITAL_LETTER_E_WITH_MACRON */
+ { { GDK_Multi_key, GDK_minus, GDK_I, 0 }, 0x012A }, /* LATIN_CAPITAL_LETTER_I_WITH_MACRON */
+ { { GDK_Multi_key, GDK_minus, GDK_L, 0 }, 0x00A3 }, /* POUND_SIGN */
+ { { GDK_Multi_key, GDK_minus, GDK_N, 0 }, 0x00D1 }, /* LATIN_CAPITAL_LETTER_N_WITH_TILDE */
+ { { GDK_Multi_key, GDK_minus, GDK_O, 0 }, 0x00D5 }, /* LATIN_CAPITAL_LETTER_O_WITH_TILDE */
+ { { GDK_Multi_key, GDK_minus, GDK_U, 0 }, 0x00D9 }, /* LATIN_CAPITAL_LETTER_U_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_minus, GDK_Y, 0 }, 0x00A5 }, /* YEN_SIGN */
+ { { GDK_Multi_key, GDK_minus, GDK_asciicircum, 0 }, 0x00AF }, /* MACRON */
+ { { GDK_Multi_key, GDK_minus, GDK_a, 0 }, 0x00E3 }, /* LATIN_SMALL_LETTER_A_WITH_TILDE */
+ { { GDK_Multi_key, GDK_minus, GDK_d, 0 }, 0x0111 }, /* LATIN_SMALL_LETTER_D_WITH_STROKE */
+ { { GDK_Multi_key, GDK_minus, GDK_e, 0 }, 0x0113 }, /* LATIN_SMALL_LETTER_E_WITH_MACRON */
+ { { GDK_Multi_key, GDK_minus, GDK_i, 0 }, 0x012B }, /* LATIN_SMALL_LETTER_I_WITH_MACRON */
+ { { GDK_Multi_key, GDK_minus, GDK_l, 0 }, 0x00A3 }, /* POUND_SIGN */
+ { { GDK_Multi_key, GDK_minus, GDK_n, 0 }, 0x00F1 }, /* LATIN_SMALL_LETTER_N_WITH_TILDE */
+ { { GDK_Multi_key, GDK_minus, GDK_o, 0 }, 0x00F5 }, /* LATIN_SMALL_LETTER_O_WITH_TILDE */
+ { { GDK_Multi_key, GDK_minus, GDK_u, 0 }, 0x016B }, /* LATIN_SMALL_LETTER_U_WITH_MACRON */
+ { { GDK_Multi_key, GDK_minus, GDK_y, 0 }, 0x00A5 }, /* YEN_SIGN */
+ { { GDK_Multi_key, GDK_period, GDK_period, 0 }, 0x02D9 }, /* DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_B, 0 }, 0x1E02 }, /* LATIN_CAPITAL_LETTER_B_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_C, 0 }, 0x010A }, /* LATIN_CAPITAL_LETTER_C_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_D, 0 }, 0x1E0A }, /* LATIN_CAPITAL_LETTER_D_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_E, 0 }, 0x0116 }, /* LATIN_CAPITAL_LETTER_E_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_F, 0 }, 0x1E1E }, /* LATIN_CAPITAL_LETTER_F_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_G, 0 }, 0x0120 }, /* LATIN_CAPITAL_LETTER_G_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_I, 0 }, 0x0130 }, /* LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_M, 0 }, 0x1E40 }, /* LATIN_CAPITAL_LETTER_M_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_P, 0 }, 0x1E56 }, /* LATIN_CAPITAL_LETTER_P_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_S, 0 }, 0x1E60 }, /* LATIN_CAPITAL_LETTER_S_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_T, 0 }, 0x1E6A }, /* LATIN_CAPITAL_LETTER_T_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_asciicircum, 0 }, 0x00B7 }, /* MIDDLE_DOT */
+ { { GDK_Multi_key, GDK_period, GDK_b, 0 }, 0x1E03 }, /* LATIN_SMALL_LETTER_B_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_c, 0 }, 0x010B }, /* LATIN_SMALL_LETTER_C_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_d, 0 }, 0x1E0B }, /* LATIN_SMALL_LETTER_D_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_e, 0 }, 0x0117 }, /* LATIN_SMALL_LETTER_E_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_f, 0 }, 0x1E1F }, /* LATIN_SMALL_LETTER_F_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_g, 0 }, 0x0121 }, /* LATIN_SMALL_LETTER_G_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_i, 0 }, 0x0131 }, /* LATIN_SMALL_LETTER_DOTLESS_I */
+ { { GDK_Multi_key, GDK_period, GDK_m, 0 }, 0x1E41 }, /* LATIN_SMALL_LETTER_M_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_p, 0 }, 0x1E57 }, /* LATIN_SMALL_LETTER_P_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_s, 0 }, 0x1E61 }, /* LATIN_SMALL_LETTER_S_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_period, GDK_t, 0 }, 0x1E6B }, /* LATIN_SMALL_LETTER_T_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_slash, GDK_slash, 0 }, 0x005C }, /* REVERSE_SOLIDUS */
+ { { GDK_Multi_key, GDK_slash, GDK_less, 0 }, 0x005C }, /* REVERSE_SOLIDUS */
+ { { GDK_Multi_key, GDK_slash, GDK_C, 0 }, 0x00A2 }, /* CENT_SIGN */
+ { { GDK_Multi_key, GDK_slash, GDK_O, 0 }, 0x00D8 }, /* LATIN_CAPITAL_LETTER_O_WITH_STROKE */
+ { { GDK_Multi_key, GDK_slash, GDK_T, 0 }, 0x0166 }, /* LATIN_CAPITAL_LETTER_T_WITH_STROKE */
+ { { GDK_Multi_key, GDK_slash, GDK_U, 0 }, 0x00B5 }, /* MICRO_SIGN */
+ { { GDK_Multi_key, GDK_slash, GDK_asciicircum, 0 }, 0x007C }, /* VERTICAL_LINE */
+ { { GDK_Multi_key, GDK_slash, GDK_c, 0 }, 0x00A2 }, /* CENT_SIGN */
+ { { GDK_Multi_key, GDK_slash, GDK_o, 0 }, 0x00F8 }, /* LATIN_SMALL_LETTER_O_WITH_STROKE */
+ { { GDK_Multi_key, GDK_slash, GDK_t, 0 }, 0x0167 }, /* LATIN_SMALL_LETTER_T_WITH_STROKE */
+ { { GDK_Multi_key, GDK_slash, GDK_u, 0 }, 0x00B5 }, /* MICRO_SIGN */
+ { { GDK_Multi_key, GDK_0, GDK_asterisk, 0 }, 0x00B0 }, /* DEGREE_SIGN */
+ { { GDK_Multi_key, GDK_0, GDK_C, 0 }, 0x00A9 }, /* COPYRIGHT_SIGN */
+ { { GDK_Multi_key, GDK_0, GDK_S, 0 }, 0x00A7 }, /* SECTION_SIGN */
+ { { GDK_Multi_key, GDK_0, GDK_X, 0 }, 0x00A4 }, /* CURRENCY_SIGN */
+ { { GDK_Multi_key, GDK_0, GDK_asciicircum, 0 }, 0x00B0 }, /* DEGREE_SIGN */
+ { { GDK_Multi_key, GDK_0, GDK_c, 0 }, 0x00A9 }, /* COPYRIGHT_SIGN */
+ { { GDK_Multi_key, GDK_0, GDK_s, 0 }, 0x00A7 }, /* SECTION_SIGN */
+ { { GDK_Multi_key, GDK_0, GDK_x, 0 }, 0x00A4 }, /* CURRENCY_SIGN */
+ { { GDK_Multi_key, GDK_1, GDK_S, 0 }, 0x00B9 }, /* SUPERSCRIPT_ONE */
+ { { GDK_Multi_key, GDK_1, GDK_asciicircum, 0 }, 0x00B9 }, /* SUPERSCRIPT_ONE */
+ { { GDK_Multi_key, GDK_1, GDK_s, 0 }, 0x00B9 }, /* SUPERSCRIPT_ONE */
+ { { GDK_Multi_key, GDK_2, GDK_S, 0 }, 0x00B2 }, /* SUPERSCRIPT_TWO */
+ { { GDK_Multi_key, GDK_2, GDK_asciicircum, 0 }, 0x00B2 }, /* SUPERSCRIPT_TWO */
+ { { GDK_Multi_key, GDK_2, GDK_s, 0 }, 0x00B2 }, /* SUPERSCRIPT_TWO */
+ { { GDK_Multi_key, GDK_3, GDK_S, 0 }, 0x00B3 }, /* SUPERSCRIPT_THREE */
+ { { GDK_Multi_key, GDK_3, GDK_asciicircum, 0 }, 0x00B3 }, /* SUPERSCRIPT_THREE */
+ { { GDK_Multi_key, GDK_3, GDK_s, 0 }, 0x00B3 }, /* SUPERSCRIPT_THREE */
+ { { GDK_Multi_key, GDK_colon, GDK_minus, 0 }, 0x00F7 }, /* DIVISION_SIGN */
+ { { GDK_Multi_key, GDK_less, GDK_slash, 0 }, 0x005C }, /* REVERSE_SOLIDUS */
+ { { GDK_Multi_key, GDK_less, GDK_less, 0 }, 0x00AB }, /* LEFTxPOINTING_DOUBLE_ANGLE_QUOTATION_MARK */
+ { { GDK_Multi_key, GDK_less, GDK_C, 0 }, 0x010C }, /* LATIN_CAPITAL_LETTER_C_WITH_CARON */
+ { { GDK_Multi_key, GDK_less, GDK_S, 0 }, 0x0160 }, /* LATIN_CAPITAL_LETTER_S_WITH_CARON */
+ { { GDK_Multi_key, GDK_less, GDK_Z, 0 }, 0x017D }, /* LATIN_CAPITAL_LETTER_Z_WITH_CARON */
+ { { GDK_Multi_key, GDK_less, GDK_c, 0 }, 0x010D }, /* LATIN_SMALL_LETTER_C_WITH_CARON */
+ { { GDK_Multi_key, GDK_less, GDK_s, 0 }, 0x0161 }, /* LATIN_SMALL_LETTER_S_WITH_CARON */
+ { { GDK_Multi_key, GDK_less, GDK_z, 0 }, 0x017E }, /* LATIN_SMALL_LETTER_Z_WITH_CARON */
+ { { GDK_Multi_key, GDK_equal, GDK_C, 0 }, 0x20AC }, /* EURO_SIGN */
+ { { GDK_Multi_key, GDK_equal, GDK_L, 0 }, 0x00A3 }, /* POUND_SIGN */
+ { { GDK_Multi_key, GDK_equal, GDK_Y, 0 }, 0x00A5 }, /* YEN_SIGN */
+ { { GDK_Multi_key, GDK_equal, GDK_e, 0 }, 0x20AC }, /* EURO_SIGN */
+ { { GDK_Multi_key, GDK_equal, GDK_l, 0 }, 0x00A3 }, /* POUND_SIGN */
+ { { GDK_Multi_key, GDK_equal, GDK_y, 0 }, 0x00A5 }, /* YEN_SIGN */
+ { { GDK_Multi_key, GDK_greater, GDK_space, 0 }, 0x005E }, /* CIRCUMFLEX_ACCENT */
+ { { GDK_Multi_key, GDK_greater, GDK_greater, 0 }, 0x00BB }, /* RIGHTxPOINTING_DOUBLE_ANGLE_QUOTATION_MARK */
+ { { GDK_Multi_key, GDK_greater, GDK_A, 0 }, 0x00C2 }, /* LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_greater, GDK_E, 0 }, 0x00CA }, /* LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_greater, GDK_I, 0 }, 0x00CE }, /* LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_greater, GDK_O, 0 }, 0x00D4 }, /* LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_greater, GDK_U, 0 }, 0x00DB }, /* LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_greater, GDK_a, 0 }, 0x00E2 }, /* LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_greater, GDK_e, 0 }, 0x00EA }, /* LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_greater, GDK_i, 0 }, 0x00EE }, /* LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_greater, GDK_o, 0 }, 0x00F4 }, /* LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_greater, GDK_u, 0 }, 0x00FB }, /* LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_question, GDK_question, 0 }, 0x00BF }, /* INVERTED_QUESTION_MARK */
+ { { GDK_Multi_key, GDK_A, GDK_quotedbl, 0 }, 0x00C4 }, /* LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_A, GDK_apostrophe, 0 }, 0x00C1 }, /* LATIN_CAPITAL_LETTER_A_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_A, GDK_asterisk, 0 }, 0x00C5 }, /* LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE */
+ { { GDK_Multi_key, GDK_A, GDK_comma, 0 }, 0x0104 }, /* LATIN_CAPITAL_LETTER_A_WITH_OGONEK */
+ { { GDK_Multi_key, GDK_A, GDK_minus, 0 }, 0x00C3 }, /* LATIN_CAPITAL_LETTER_A_WITH_TILDE */
+ { { GDK_Multi_key, GDK_A, GDK_greater, 0 }, 0x00C2 }, /* LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_A, GDK_A, 0 }, 0x00C5 }, /* LATIN_CAPITAL_LETTER_A_WITH_RING_ABOVE */
+ { { GDK_Multi_key, GDK_A, GDK_E, 0 }, 0x00C6 }, /* LATIN_CAPITAL_LETTER_AE */
+ { { GDK_Multi_key, GDK_A, GDK_asciicircum, 0 }, 0x00C2 }, /* LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_A, GDK_underscore, 0 }, 0x00AA }, /* FEMININE_ORDINAL_INDICATOR */
+ { { GDK_Multi_key, GDK_A, GDK_grave, 0 }, 0x00C0 }, /* LATIN_CAPITAL_LETTER_A_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_A, GDK_asciitilde, 0 }, 0x00C3 }, /* LATIN_CAPITAL_LETTER_A_WITH_TILDE */
+ { { GDK_Multi_key, GDK_A, GDK_diaeresis, 0 }, 0x00C4 }, /* LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_A, GDK_acute, 0 }, 0x00C1 }, /* LATIN_CAPITAL_LETTER_A_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_B, GDK_period, 0 }, 0x1E02 }, /* LATIN_CAPITAL_LETTER_B_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_C, GDK_comma, 0 }, 0x00C7 }, /* LATIN_CAPITAL_LETTER_C_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_C, GDK_period, 0 }, 0x010A }, /* LATIN_CAPITAL_LETTER_C_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_C, GDK_slash, 0 }, 0x00A2 }, /* CENT_SIGN */
+ { { GDK_Multi_key, GDK_C, GDK_0, 0 }, 0x00A9 }, /* COPYRIGHT_SIGN */
+ { { GDK_Multi_key, GDK_C, GDK_less, 0 }, 0x010C }, /* LATIN_CAPITAL_LETTER_C_WITH_CARON */
+ { { GDK_Multi_key, GDK_C, GDK_equal, 0 }, 0x20AC }, /* EURO_SIGN */
+ { { GDK_Multi_key, GDK_C, GDK_O, 0 }, 0x00A9 }, /* COPYRIGHT_SIGN */
+ { { GDK_Multi_key, GDK_C, GDK_o, 0 }, 0x00A9 }, /* COPYRIGHT_SIGN */
+ { { GDK_Multi_key, GDK_C, GDK_bar, 0 }, 0x00A2 }, /* CENT_SIGN */
+ { { GDK_Multi_key, GDK_D, GDK_minus, 0 }, 0x0110 }, /* LATIN_CAPITAL_LETTER_D_WITH_STROKE */
+ { { GDK_Multi_key, GDK_D, GDK_period, 0 }, 0x1E0A }, /* LATIN_CAPITAL_LETTER_D_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_E, GDK_quotedbl, 0 }, 0x00CB }, /* LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_E, GDK_apostrophe, 0 }, 0x00C9 }, /* LATIN_CAPITAL_LETTER_E_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_E, GDK_comma, 0 }, 0x0118 }, /* LATIN_CAPITAL_LETTER_E_WITH_OGONEK */
+ { { GDK_Multi_key, GDK_E, GDK_minus, 0 }, 0x0112 }, /* LATIN_CAPITAL_LETTER_E_WITH_MACRON */
+ { { GDK_Multi_key, GDK_E, GDK_period, 0 }, 0x0116 }, /* LATIN_CAPITAL_LETTER_E_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_E, GDK_greater, 0 }, 0x00CA }, /* LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_E, GDK_asciicircum, 0 }, 0x00CA }, /* LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_E, GDK_underscore, 0 }, 0x0112 }, /* LATIN_CAPITAL_LETTER_E_WITH_MACRON */
+ { { GDK_Multi_key, GDK_E, GDK_grave, 0 }, 0x00C8 }, /* LATIN_CAPITAL_LETTER_E_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_E, GDK_diaeresis, 0 }, 0x00CB }, /* LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_E, GDK_acute, 0 }, 0x00C9 }, /* LATIN_CAPITAL_LETTER_E_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_F, GDK_period, 0 }, 0x1E1E }, /* LATIN_CAPITAL_LETTER_F_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_G, GDK_parenleft, 0 }, 0x011E }, /* LATIN_CAPITAL_LETTER_G_WITH_BREVE */
+ { { GDK_Multi_key, GDK_G, GDK_comma, 0 }, 0x0122 }, /* LATIN_CAPITAL_LETTER_G_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_G, GDK_period, 0 }, 0x0120 }, /* LATIN_CAPITAL_LETTER_G_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_G, GDK_U, 0 }, 0x011E }, /* LATIN_CAPITAL_LETTER_G_WITH_BREVE */
+ { { GDK_Multi_key, GDK_G, GDK_breve, 0 }, 0x011E }, /* LATIN_CAPITAL_LETTER_G_WITH_BREVE */
+ { { GDK_Multi_key, GDK_I, GDK_quotedbl, 0 }, 0x00CF }, /* LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_I, GDK_apostrophe, 0 }, 0x00CD }, /* LATIN_CAPITAL_LETTER_I_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_I, GDK_comma, 0 }, 0x012E }, /* LATIN_CAPITAL_LETTER_I_WITH_OGONEK */
+ { { GDK_Multi_key, GDK_I, GDK_minus, 0 }, 0x012A }, /* LATIN_CAPITAL_LETTER_I_WITH_MACRON */
+ { { GDK_Multi_key, GDK_I, GDK_period, 0 }, 0x0130 }, /* LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_I, GDK_greater, 0 }, 0x00CE }, /* LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_I, GDK_asciicircum, 0 }, 0x00CE }, /* LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_I, GDK_underscore, 0 }, 0x012A }, /* LATIN_CAPITAL_LETTER_I_WITH_MACRON */
+ { { GDK_Multi_key, GDK_I, GDK_grave, 0 }, 0x00CC }, /* LATIN_CAPITAL_LETTER_I_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_I, GDK_asciitilde, 0 }, 0x0128 }, /* LATIN_CAPITAL_LETTER_I_WITH_TILDE */
+ { { GDK_Multi_key, GDK_I, GDK_diaeresis, 0 }, 0x00CF }, /* LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_I, GDK_acute, 0 }, 0x00CD }, /* LATIN_CAPITAL_LETTER_I_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_K, GDK_comma, 0 }, 0x0136 }, /* LATIN_CAPITAL_LETTER_K_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_L, GDK_comma, 0 }, 0x013B }, /* LATIN_CAPITAL_LETTER_L_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_L, GDK_minus, 0 }, 0x00A3 }, /* POUND_SIGN */
+ { { GDK_Multi_key, GDK_L, GDK_equal, 0 }, 0x00A3 }, /* POUND_SIGN */
+ { { GDK_Multi_key, GDK_L, GDK_V, 0 }, 0x007C }, /* VERTICAL_LINE */
+ { { GDK_Multi_key, GDK_M, GDK_period, 0 }, 0x1E40 }, /* LATIN_CAPITAL_LETTER_M_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_N, GDK_comma, 0 }, 0x0145 }, /* LATIN_CAPITAL_LETTER_N_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_N, GDK_minus, 0 }, 0x00D1 }, /* LATIN_CAPITAL_LETTER_N_WITH_TILDE */
+ { { GDK_Multi_key, GDK_N, GDK_G, 0 }, 0x014A }, /* LATIN_CAPITAL_LETTER_ENG */
+ { { GDK_Multi_key, GDK_N, GDK_asciitilde, 0 }, 0x00D1 }, /* LATIN_CAPITAL_LETTER_N_WITH_TILDE */
+ { { GDK_Multi_key, GDK_O, GDK_quotedbl, 0 }, 0x00D6 }, /* LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_O, GDK_apostrophe, 0 }, 0x00D3 }, /* LATIN_CAPITAL_LETTER_O_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_O, GDK_minus, 0 }, 0x00D5 }, /* LATIN_CAPITAL_LETTER_O_WITH_TILDE */
+ { { GDK_Multi_key, GDK_O, GDK_slash, 0 }, 0x00D8 }, /* LATIN_CAPITAL_LETTER_O_WITH_STROKE */
+ { { GDK_Multi_key, GDK_O, GDK_greater, 0 }, 0x00D4 }, /* LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_O, GDK_C, 0 }, 0x00A9 }, /* COPYRIGHT_SIGN */
+ { { GDK_Multi_key, GDK_O, GDK_E, 0 }, 0x0152 }, /* LATIN_CAPITAL_LIGATURE_OE */
+ { { GDK_Multi_key, GDK_O, GDK_R, 0 }, 0x00AE }, /* REGISTERED_SIGN */
+ { { GDK_Multi_key, GDK_O, GDK_S, 0 }, 0x00A7 }, /* SECTION_SIGN */
+ { { GDK_Multi_key, GDK_O, GDK_X, 0 }, 0x00A4 }, /* CURRENCY_SIGN */
+ { { GDK_Multi_key, GDK_O, GDK_asciicircum, 0 }, 0x00D4 }, /* LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_O, GDK_underscore, 0 }, 0x00BA }, /* MASCULINE_ORDINAL_INDICATOR */
+ { { GDK_Multi_key, GDK_O, GDK_grave, 0 }, 0x00D2 }, /* LATIN_CAPITAL_LETTER_O_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_O, GDK_c, 0 }, 0x00A9 }, /* COPYRIGHT_SIGN */
+ { { GDK_Multi_key, GDK_O, GDK_x, 0 }, 0x00A4 }, /* CURRENCY_SIGN */
+ { { GDK_Multi_key, GDK_O, GDK_asciitilde, 0 }, 0x00D5 }, /* LATIN_CAPITAL_LETTER_O_WITH_TILDE */
+ { { GDK_Multi_key, GDK_O, GDK_diaeresis, 0 }, 0x00D6 }, /* LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_O, GDK_acute, 0 }, 0x00D3 }, /* LATIN_CAPITAL_LETTER_O_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_P, GDK_exclam, 0 }, 0x00B6 }, /* PILCROW_SIGN */
+ { { GDK_Multi_key, GDK_P, GDK_period, 0 }, 0x1E56 }, /* LATIN_CAPITAL_LETTER_P_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_R, GDK_comma, 0 }, 0x0156 }, /* LATIN_CAPITAL_LETTER_R_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_R, GDK_O, 0 }, 0x00AE }, /* REGISTERED_SIGN */
+ { { GDK_Multi_key, GDK_S, GDK_exclam, 0 }, 0x00A7 }, /* SECTION_SIGN */
+ { { GDK_Multi_key, GDK_S, GDK_comma, 0 }, 0x015E }, /* LATIN_CAPITAL_LETTER_S_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_S, GDK_period, 0 }, 0x1E60 }, /* LATIN_CAPITAL_LETTER_S_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_S, GDK_0, 0 }, 0x00A7 }, /* SECTION_SIGN */
+ { { GDK_Multi_key, GDK_S, GDK_1, 0 }, 0x00B9 }, /* SUPERSCRIPT_ONE */
+ { { GDK_Multi_key, GDK_S, GDK_2, 0 }, 0x00B2 }, /* SUPERSCRIPT_TWO */
+ { { GDK_Multi_key, GDK_S, GDK_3, 0 }, 0x00B3 }, /* SUPERSCRIPT_THREE */
+ { { GDK_Multi_key, GDK_S, GDK_less, 0 }, 0x0160 }, /* LATIN_CAPITAL_LETTER_S_WITH_CARON */
+ { { GDK_Multi_key, GDK_S, GDK_O, 0 }, 0x00A7 }, /* SECTION_SIGN */
+ { { GDK_Multi_key, GDK_S, GDK_cedilla, 0 }, 0x015E }, /* LATIN_CAPITAL_LETTER_S_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_T, GDK_minus, 0 }, 0x0166 }, /* LATIN_CAPITAL_LETTER_T_WITH_STROKE */
+ { { GDK_Multi_key, GDK_T, GDK_period, 0 }, 0x1E6A }, /* LATIN_CAPITAL_LETTER_T_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_T, GDK_slash, 0 }, 0x0166 }, /* LATIN_CAPITAL_LETTER_T_WITH_STROKE */
+ { { GDK_Multi_key, GDK_T, GDK_H, 0 }, 0x00DE }, /* LATIN_CAPITAL_LETTER_THORN */
+ { { GDK_Multi_key, GDK_U, GDK_quotedbl, 0 }, 0x00DC }, /* LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_U, GDK_apostrophe, 0 }, 0x00DA }, /* LATIN_CAPITAL_LETTER_U_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_U, GDK_comma, 0 }, 0x0172 }, /* LATIN_CAPITAL_LETTER_U_WITH_OGONEK */
+ { { GDK_Multi_key, GDK_U, GDK_minus, 0 }, 0x00D9 }, /* LATIN_CAPITAL_LETTER_U_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_U, GDK_slash, 0 }, 0x00B5 }, /* MICRO_SIGN */
+ { { GDK_Multi_key, GDK_U, GDK_greater, 0 }, 0x00DB }, /* LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_U, GDK_asciicircum, 0 }, 0x00DB }, /* LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_U, GDK_underscore, 0 }, 0x00D9 }, /* LATIN_CAPITAL_LETTER_U_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_U, GDK_grave, 0 }, 0x00D9 }, /* LATIN_CAPITAL_LETTER_U_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_U, GDK_asciitilde, 0 }, 0x0168 }, /* LATIN_CAPITAL_LETTER_U_WITH_TILDE */
+ { { GDK_Multi_key, GDK_U, GDK_diaeresis, 0 }, 0x00DC }, /* LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_U, GDK_acute, 0 }, 0x00DA }, /* LATIN_CAPITAL_LETTER_U_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_V, GDK_L, 0 }, 0x007C }, /* VERTICAL_LINE */
+ { { GDK_Multi_key, GDK_W, GDK_asciicircum, 0 }, 0x0174 }, /* LATIN_CAPITAL_LETTER_W_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_X, GDK_0, 0 }, 0x00A4 }, /* CURRENCY_SIGN */
+ { { GDK_Multi_key, GDK_X, GDK_O, 0 }, 0x00A4 }, /* CURRENCY_SIGN */
+ { { GDK_Multi_key, GDK_X, GDK_o, 0 }, 0x00A4 }, /* CURRENCY_SIGN */
+ { { GDK_Multi_key, GDK_Y, GDK_quotedbl, 0 }, 0x0178 }, /* LATIN_CAPITAL_LETTER_Y_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_Y, GDK_apostrophe, 0 }, 0x00DD }, /* LATIN_CAPITAL_LETTER_Y_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_Y, GDK_minus, 0 }, 0x00A5 }, /* YEN_SIGN */
+ { { GDK_Multi_key, GDK_Y, GDK_equal, 0 }, 0x00A5 }, /* YEN_SIGN */
+ { { GDK_Multi_key, GDK_Y, GDK_asciicircum, 0 }, 0x0176 }, /* LATIN_CAPITAL_LETTER_Y_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_Y, GDK_diaeresis, 0 }, 0x0178 }, /* LATIN_CAPITAL_LETTER_Y_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_Y, GDK_acute, 0 }, 0x00DD }, /* LATIN_CAPITAL_LETTER_Y_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_Z, GDK_less, 0 }, 0x017D }, /* LATIN_CAPITAL_LETTER_Z_WITH_CARON */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_space, 0 }, 0x005E }, /* CIRCUMFLEX_ACCENT */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_minus, 0 }, 0x00AF }, /* MACRON */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_period, 0 }, 0x00B7 }, /* MIDDLE_DOT */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_slash, 0 }, 0x007C }, /* VERTICAL_LINE */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_0, 0 }, 0x00B0 }, /* DEGREE_SIGN */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_1, 0 }, 0x00B9 }, /* SUPERSCRIPT_ONE */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_2, 0 }, 0x00B2 }, /* SUPERSCRIPT_TWO */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_3, 0 }, 0x00B3 }, /* SUPERSCRIPT_THREE */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_A, 0 }, 0x00C2 }, /* LATIN_CAPITAL_LETTER_A_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_E, 0 }, 0x00CA }, /* LATIN_CAPITAL_LETTER_E_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_I, 0 }, 0x00CE }, /* LATIN_CAPITAL_LETTER_I_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_O, 0 }, 0x00D4 }, /* LATIN_CAPITAL_LETTER_O_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_U, 0 }, 0x00DB }, /* LATIN_CAPITAL_LETTER_U_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_W, 0 }, 0x0174 }, /* LATIN_CAPITAL_LETTER_W_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_Y, 0 }, 0x0176 }, /* LATIN_CAPITAL_LETTER_Y_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_underscore, 0 }, 0x00AF }, /* MACRON */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_a, 0 }, 0x00E2 }, /* LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_e, 0 }, 0x00EA }, /* LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_i, 0 }, 0x00EE }, /* LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_o, 0 }, 0x00F4 }, /* LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_u, 0 }, 0x00FB }, /* LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_w, 0 }, 0x0175 }, /* LATIN_SMALL_LETTER_W_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_asciicircum, GDK_y, 0 }, 0x0177 }, /* LATIN_SMALL_LETTER_Y_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_underscore, GDK_A, 0 }, 0x00AA }, /* FEMININE_ORDINAL_INDICATOR */
+ { { GDK_Multi_key, GDK_underscore, GDK_E, 0 }, 0x0112 }, /* LATIN_CAPITAL_LETTER_E_WITH_MACRON */
+ { { GDK_Multi_key, GDK_underscore, GDK_I, 0 }, 0x012A }, /* LATIN_CAPITAL_LETTER_I_WITH_MACRON */
+ { { GDK_Multi_key, GDK_underscore, GDK_O, 0 }, 0x00BA }, /* MASCULINE_ORDINAL_INDICATOR */
+ { { GDK_Multi_key, GDK_underscore, GDK_U, 0 }, 0x00D9 }, /* LATIN_CAPITAL_LETTER_U_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_underscore, GDK_asciicircum, 0 }, 0x00AF }, /* MACRON */
+ { { GDK_Multi_key, GDK_underscore, GDK_underscore, 0 }, 0x00AF }, /* MACRON */
+ { { GDK_Multi_key, GDK_underscore, GDK_a, 0 }, 0x00AA }, /* FEMININE_ORDINAL_INDICATOR */
+ { { GDK_Multi_key, GDK_underscore, GDK_e, 0 }, 0x0113 }, /* LATIN_SMALL_LETTER_E_WITH_MACRON */
+ { { GDK_Multi_key, GDK_underscore, GDK_i, 0 }, 0x012B }, /* LATIN_SMALL_LETTER_I_WITH_MACRON */
+ { { GDK_Multi_key, GDK_underscore, GDK_o, 0 }, 0x00BA }, /* MASCULINE_ORDINAL_INDICATOR */
+ { { GDK_Multi_key, GDK_underscore, GDK_u, 0 }, 0x016B }, /* LATIN_SMALL_LETTER_U_WITH_MACRON */
+ { { GDK_Multi_key, GDK_grave, GDK_space, 0 }, 0x0060 }, /* GRAVE_ACCENT */
+ { { GDK_Multi_key, GDK_grave, GDK_A, 0 }, 0x00C0 }, /* LATIN_CAPITAL_LETTER_A_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_grave, GDK_E, 0 }, 0x00C8 }, /* LATIN_CAPITAL_LETTER_E_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_grave, GDK_I, 0 }, 0x00CC }, /* LATIN_CAPITAL_LETTER_I_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_grave, GDK_O, 0 }, 0x00D2 }, /* LATIN_CAPITAL_LETTER_O_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_grave, GDK_U, 0 }, 0x00D9 }, /* LATIN_CAPITAL_LETTER_U_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_grave, GDK_a, 0 }, 0x00E0 }, /* LATIN_SMALL_LETTER_A_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_grave, GDK_e, 0 }, 0x00E8 }, /* LATIN_SMALL_LETTER_E_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_grave, GDK_i, 0 }, 0x00EC }, /* LATIN_SMALL_LETTER_I_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_grave, GDK_o, 0 }, 0x00F2 }, /* LATIN_SMALL_LETTER_O_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_grave, GDK_u, 0 }, 0x00F9 }, /* LATIN_SMALL_LETTER_U_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_a, GDK_quotedbl, 0 }, 0x00E4 }, /* LATIN_SMALL_LETTER_A_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_a, GDK_apostrophe, 0 }, 0x00E1 }, /* LATIN_SMALL_LETTER_A_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_a, GDK_asterisk, 0 }, 0x00E5 }, /* LATIN_SMALL_LETTER_A_WITH_RING_ABOVE */
+ { { GDK_Multi_key, GDK_a, GDK_comma, 0 }, 0x0105 }, /* LATIN_SMALL_LETTER_A_WITH_OGONEK */
+ { { GDK_Multi_key, GDK_a, GDK_minus, 0 }, 0x00E3 }, /* LATIN_SMALL_LETTER_A_WITH_TILDE */
+ { { GDK_Multi_key, GDK_a, GDK_greater, 0 }, 0x00E2 }, /* LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_a, GDK_asciicircum, 0 }, 0x00E2 }, /* LATIN_SMALL_LETTER_A_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_a, GDK_underscore, 0 }, 0x00AA }, /* FEMININE_ORDINAL_INDICATOR */
+ { { GDK_Multi_key, GDK_a, GDK_grave, 0 }, 0x00E0 }, /* LATIN_SMALL_LETTER_A_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_a, GDK_a, 0 }, 0x00E5 }, /* LATIN_SMALL_LETTER_A_WITH_RING_ABOVE */
+ { { GDK_Multi_key, GDK_a, GDK_e, 0 }, 0x00E6 }, /* LATIN_SMALL_LETTER_AE */
+ { { GDK_Multi_key, GDK_a, GDK_asciitilde, 0 }, 0x00E3 }, /* LATIN_SMALL_LETTER_A_WITH_TILDE */
+ { { GDK_Multi_key, GDK_a, GDK_diaeresis, 0 }, 0x00E4 }, /* LATIN_SMALL_LETTER_A_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_a, GDK_acute, 0 }, 0x00E1 }, /* LATIN_SMALL_LETTER_A_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_b, GDK_period, 0 }, 0x1E03 }, /* LATIN_SMALL_LETTER_B_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_c, GDK_comma, 0 }, 0x00E7 }, /* LATIN_SMALL_LETTER_C_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_c, GDK_period, 0 }, 0x010B }, /* LATIN_SMALL_LETTER_C_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_c, GDK_slash, 0 }, 0x00A2 }, /* CENT_SIGN */
+ { { GDK_Multi_key, GDK_c, GDK_0, 0 }, 0x00A9 }, /* COPYRIGHT_SIGN */
+ { { GDK_Multi_key, GDK_c, GDK_less, 0 }, 0x010D }, /* LATIN_SMALL_LETTER_C_WITH_CARON */
+ { { GDK_Multi_key, GDK_c, GDK_O, 0 }, 0x00A9 }, /* COPYRIGHT_SIGN */
+ { { GDK_Multi_key, GDK_c, GDK_o, 0 }, 0x00A9 }, /* COPYRIGHT_SIGN */
+ { { GDK_Multi_key, GDK_c, GDK_bar, 0 }, 0x00A2 }, /* CENT_SIGN */
+ { { GDK_Multi_key, GDK_d, GDK_minus, 0 }, 0x0111 }, /* LATIN_SMALL_LETTER_D_WITH_STROKE */
+ { { GDK_Multi_key, GDK_d, GDK_period, 0 }, 0x1E0B }, /* LATIN_SMALL_LETTER_D_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_e, GDK_quotedbl, 0 }, 0x00EB }, /* LATIN_SMALL_LETTER_E_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_e, GDK_apostrophe, 0 }, 0x00E9 }, /* LATIN_SMALL_LETTER_E_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_e, GDK_comma, 0 }, 0x0119 }, /* LATIN_SMALL_LETTER_E_WITH_OGONEK */
+ { { GDK_Multi_key, GDK_e, GDK_minus, 0 }, 0x0113 }, /* LATIN_SMALL_LETTER_E_WITH_MACRON */
+ { { GDK_Multi_key, GDK_e, GDK_period, 0 }, 0x0117 }, /* LATIN_SMALL_LETTER_E_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_e, GDK_equal, 0 }, 0x20AC }, /* EURO_SIGN */
+ { { GDK_Multi_key, GDK_e, GDK_greater, 0 }, 0x00EA }, /* LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_e, GDK_asciicircum, 0 }, 0x00EA }, /* LATIN_SMALL_LETTER_E_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_e, GDK_underscore, 0 }, 0x0113 }, /* LATIN_SMALL_LETTER_E_WITH_MACRON */
+ { { GDK_Multi_key, GDK_e, GDK_grave, 0 }, 0x00E8 }, /* LATIN_SMALL_LETTER_E_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_e, GDK_diaeresis, 0 }, 0x00EB }, /* LATIN_SMALL_LETTER_E_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_e, GDK_acute, 0 }, 0x00E9 }, /* LATIN_SMALL_LETTER_E_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_f, GDK_period, 0 }, 0x1E1F }, /* LATIN_SMALL_LETTER_F_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_g, GDK_parenleft, 0 }, 0x011F }, /* LATIN_SMALL_LETTER_G_WITH_BREVE */
+ { { GDK_Multi_key, GDK_g, GDK_comma, 0 }, 0x0123 }, /* LATIN_SMALL_LETTER_G_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_g, GDK_period, 0 }, 0x0121 }, /* LATIN_SMALL_LETTER_G_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_g, GDK_U, 0 }, 0x011F }, /* LATIN_SMALL_LETTER_G_WITH_BREVE */
+ { { GDK_Multi_key, GDK_g, GDK_breve, 0 }, 0x011F }, /* LATIN_SMALL_LETTER_G_WITH_BREVE */
+ { { GDK_Multi_key, GDK_i, GDK_quotedbl, 0 }, 0x00EF }, /* LATIN_SMALL_LETTER_I_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_i, GDK_apostrophe, 0 }, 0x00ED }, /* LATIN_SMALL_LETTER_I_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_i, GDK_comma, 0 }, 0x012F }, /* LATIN_SMALL_LETTER_I_WITH_OGONEK */
+ { { GDK_Multi_key, GDK_i, GDK_minus, 0 }, 0x012B }, /* LATIN_SMALL_LETTER_I_WITH_MACRON */
+ { { GDK_Multi_key, GDK_i, GDK_period, 0 }, 0x0131 }, /* LATIN_SMALL_LETTER_DOTLESS_I */
+ { { GDK_Multi_key, GDK_i, GDK_greater, 0 }, 0x00EE }, /* LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_i, GDK_asciicircum, 0 }, 0x00EE }, /* LATIN_SMALL_LETTER_I_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_i, GDK_underscore, 0 }, 0x012B }, /* LATIN_SMALL_LETTER_I_WITH_MACRON */
+ { { GDK_Multi_key, GDK_i, GDK_grave, 0 }, 0x00EC }, /* LATIN_SMALL_LETTER_I_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_i, GDK_asciitilde, 0 }, 0x0129 }, /* LATIN_SMALL_LETTER_I_WITH_TILDE */
+ { { GDK_Multi_key, GDK_i, GDK_diaeresis, 0 }, 0x00EF }, /* LATIN_SMALL_LETTER_I_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_i, GDK_acute, 0 }, 0x00ED }, /* LATIN_SMALL_LETTER_I_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_k, GDK_comma, 0 }, 0x0137 }, /* LATIN_SMALL_LETTER_K_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_k, GDK_k, 0 }, 0x0138 }, /* LATIN_SMALL_LETTER_KRA */
+ { { GDK_Multi_key, GDK_l, GDK_comma, 0 }, 0x013C }, /* LATIN_SMALL_LETTER_L_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_l, GDK_minus, 0 }, 0x00A3 }, /* POUND_SIGN */
+ { { GDK_Multi_key, GDK_l, GDK_equal, 0 }, 0x00A3 }, /* POUND_SIGN */
+ { { GDK_Multi_key, GDK_l, GDK_v, 0 }, 0x007C }, /* VERTICAL_LINE */
+ { { GDK_Multi_key, GDK_m, GDK_period, 0 }, 0x1E41 }, /* LATIN_SMALL_LETTER_M_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_n, GDK_comma, 0 }, 0x0146 }, /* LATIN_SMALL_LETTER_N_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_n, GDK_minus, 0 }, 0x00F1 }, /* LATIN_SMALL_LETTER_N_WITH_TILDE */
+ { { GDK_Multi_key, GDK_n, GDK_g, 0 }, 0x014B }, /* LATIN_SMALL_LETTER_ENG */
+ { { GDK_Multi_key, GDK_n, GDK_asciitilde, 0 }, 0x00F1 }, /* LATIN_SMALL_LETTER_N_WITH_TILDE */
+ { { GDK_Multi_key, GDK_o, GDK_quotedbl, 0 }, 0x00F6 }, /* LATIN_SMALL_LETTER_O_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_o, GDK_apostrophe, 0 }, 0x00F3 }, /* LATIN_SMALL_LETTER_O_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_o, GDK_minus, 0 }, 0x00F5 }, /* LATIN_SMALL_LETTER_O_WITH_TILDE */
+ { { GDK_Multi_key, GDK_o, GDK_slash, 0 }, 0x00F8 }, /* LATIN_SMALL_LETTER_O_WITH_STROKE */
+ { { GDK_Multi_key, GDK_o, GDK_greater, 0 }, 0x00F4 }, /* LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_o, GDK_C, 0 }, 0x00A9 }, /* COPYRIGHT_SIGN */
+ { { GDK_Multi_key, GDK_o, GDK_X, 0 }, 0x00A4 }, /* CURRENCY_SIGN */
+ { { GDK_Multi_key, GDK_o, GDK_asciicircum, 0 }, 0x00F4 }, /* LATIN_SMALL_LETTER_O_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_o, GDK_underscore, 0 }, 0x00BA }, /* MASCULINE_ORDINAL_INDICATOR */
+ { { GDK_Multi_key, GDK_o, GDK_grave, 0 }, 0x00F2 }, /* LATIN_SMALL_LETTER_O_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_o, GDK_c, 0 }, 0x00A9 }, /* COPYRIGHT_SIGN */
+ { { GDK_Multi_key, GDK_o, GDK_e, 0 }, 0x0153 }, /* LATIN_SMALL_LIGATURE_OE */
+ { { GDK_Multi_key, GDK_o, GDK_s, 0 }, 0x00A7 }, /* SECTION_SIGN */
+ { { GDK_Multi_key, GDK_o, GDK_x, 0 }, 0x00A4 }, /* CURRENCY_SIGN */
+ { { GDK_Multi_key, GDK_o, GDK_asciitilde, 0 }, 0x00F5 }, /* LATIN_SMALL_LETTER_O_WITH_TILDE */
+ { { GDK_Multi_key, GDK_o, GDK_diaeresis, 0 }, 0x00F6 }, /* LATIN_SMALL_LETTER_O_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_o, GDK_acute, 0 }, 0x00F3 }, /* LATIN_SMALL_LETTER_O_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_p, GDK_exclam, 0 }, 0x00B6 }, /* PILCROW_SIGN */
+ { { GDK_Multi_key, GDK_p, GDK_period, 0 }, 0x1E57 }, /* LATIN_SMALL_LETTER_P_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_r, GDK_comma, 0 }, 0x0157 }, /* LATIN_SMALL_LETTER_R_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_s, GDK_exclam, 0 }, 0x00A7 }, /* SECTION_SIGN */
+ { { GDK_Multi_key, GDK_s, GDK_comma, 0 }, 0x015F }, /* LATIN_SMALL_LETTER_S_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_s, GDK_period, 0 }, 0x1E61 }, /* LATIN_SMALL_LETTER_S_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_s, GDK_0, 0 }, 0x00A7 }, /* SECTION_SIGN */
+ { { GDK_Multi_key, GDK_s, GDK_1, 0 }, 0x00B9 }, /* SUPERSCRIPT_ONE */
+ { { GDK_Multi_key, GDK_s, GDK_2, 0 }, 0x00B2 }, /* SUPERSCRIPT_TWO */
+ { { GDK_Multi_key, GDK_s, GDK_3, 0 }, 0x00B3 }, /* SUPERSCRIPT_THREE */
+ { { GDK_Multi_key, GDK_s, GDK_less, 0 }, 0x0161 }, /* LATIN_SMALL_LETTER_S_WITH_CARON */
+ { { GDK_Multi_key, GDK_s, GDK_o, 0 }, 0x00A7 }, /* SECTION_SIGN */
+ { { GDK_Multi_key, GDK_s, GDK_s, 0 }, 0x00DF }, /* LATIN_SMALL_LETTER_SHARP_S */
+ { { GDK_Multi_key, GDK_s, GDK_cedilla, 0 }, 0x015F }, /* LATIN_SMALL_LETTER_S_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_t, GDK_minus, 0 }, 0x0167 }, /* LATIN_SMALL_LETTER_T_WITH_STROKE */
+ { { GDK_Multi_key, GDK_t, GDK_period, 0 }, 0x1E6B }, /* LATIN_SMALL_LETTER_T_WITH_DOT_ABOVE */
+ { { GDK_Multi_key, GDK_t, GDK_slash, 0 }, 0x0167 }, /* LATIN_SMALL_LETTER_T_WITH_STROKE */
+ { { GDK_Multi_key, GDK_t, GDK_h, 0 }, 0x00FE }, /* LATIN_SMALL_LETTER_THORN */
+ { { GDK_Multi_key, GDK_u, GDK_quotedbl, 0 }, 0x00FC }, /* LATIN_SMALL_LETTER_U_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_u, GDK_apostrophe, 0 }, 0x00FA }, /* LATIN_SMALL_LETTER_U_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_u, GDK_comma, 0 }, 0x0173 }, /* LATIN_SMALL_LETTER_U_WITH_OGONEK */
+ { { GDK_Multi_key, GDK_u, GDK_minus, 0 }, 0x016B }, /* LATIN_SMALL_LETTER_U_WITH_MACRON */
+ { { GDK_Multi_key, GDK_u, GDK_slash, 0 }, 0x00B5 }, /* MICRO_SIGN */
+ { { GDK_Multi_key, GDK_u, GDK_greater, 0 }, 0x00FB }, /* LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_u, GDK_asciicircum, 0 }, 0x00FB }, /* LATIN_SMALL_LETTER_U_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_u, GDK_underscore, 0 }, 0x016B }, /* LATIN_SMALL_LETTER_U_WITH_MACRON */
+ { { GDK_Multi_key, GDK_u, GDK_grave, 0 }, 0x00F9 }, /* LATIN_SMALL_LETTER_U_WITH_GRAVE */
+ { { GDK_Multi_key, GDK_u, GDK_asciitilde, 0 }, 0x0169 }, /* LATIN_SMALL_LETTER_U_WITH_TILDE */
+ { { GDK_Multi_key, GDK_u, GDK_diaeresis, 0 }, 0x00FC }, /* LATIN_SMALL_LETTER_U_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_u, GDK_acute, 0 }, 0x00FA }, /* LATIN_SMALL_LETTER_U_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_v, GDK_Z, 0 }, 0x017D }, /* LATIN_CAPITAL_LETTER_Z_WITH_CARON */
+ { { GDK_Multi_key, GDK_v, GDK_l, 0 }, 0x007C }, /* VERTICAL_LINE */
+ { { GDK_Multi_key, GDK_v, GDK_z, 0 }, 0x017E }, /* LATIN_SMALL_LETTER_Z_WITH_CARON */
+ { { GDK_Multi_key, GDK_w, GDK_asciicircum, 0 }, 0x0175 }, /* LATIN_SMALL_LETTER_W_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_x, GDK_0, 0 }, 0x00A4 }, /* CURRENCY_SIGN */
+ { { GDK_Multi_key, GDK_x, GDK_O, 0 }, 0x00A4 }, /* CURRENCY_SIGN */
+ { { GDK_Multi_key, GDK_x, GDK_o, 0 }, 0x00A4 }, /* CURRENCY_SIGN */
+ { { GDK_Multi_key, GDK_x, GDK_x, 0 }, 0x00D7 }, /* MULTIPLICATION_SIGN */
+ { { GDK_Multi_key, GDK_y, GDK_quotedbl, 0 }, 0x00FF }, /* LATIN_SMALL_LETTER_Y_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_y, GDK_apostrophe, 0 }, 0x00FD }, /* LATIN_SMALL_LETTER_Y_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_y, GDK_minus, 0 }, 0x00A5 }, /* YEN_SIGN */
+ { { GDK_Multi_key, GDK_y, GDK_equal, 0 }, 0x00A5 }, /* YEN_SIGN */
+ { { GDK_Multi_key, GDK_y, GDK_asciicircum, 0 }, 0x0177 }, /* LATIN_SMALL_LETTER_Y_WITH_CIRCUMFLEX */
+ { { GDK_Multi_key, GDK_y, GDK_diaeresis, 0 }, 0x00FF }, /* LATIN_SMALL_LETTER_Y_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_y, GDK_acute, 0 }, 0x00FD }, /* LATIN_SMALL_LETTER_Y_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_z, GDK_less, 0 }, 0x017E }, /* LATIN_SMALL_LETTER_Z_WITH_CARON */
+ { { GDK_Multi_key, GDK_bar, GDK_C, 0 }, 0x00A2 }, /* CENT_SIGN */
+ { { GDK_Multi_key, GDK_bar, GDK_c, 0 }, 0x00A2 }, /* CENT_SIGN */
+ { { GDK_Multi_key, GDK_asciitilde, GDK_space, 0 }, 0x007E }, /* TILDE */
+ { { GDK_Multi_key, GDK_asciitilde, GDK_A, 0 }, 0x00C3 }, /* LATIN_CAPITAL_LETTER_A_WITH_TILDE */
+ { { GDK_Multi_key, GDK_asciitilde, GDK_I, 0 }, 0x0128 }, /* LATIN_CAPITAL_LETTER_I_WITH_TILDE */
+ { { GDK_Multi_key, GDK_asciitilde, GDK_N, 0 }, 0x00D1 }, /* LATIN_CAPITAL_LETTER_N_WITH_TILDE */
+ { { GDK_Multi_key, GDK_asciitilde, GDK_O, 0 }, 0x00D5 }, /* LATIN_CAPITAL_LETTER_O_WITH_TILDE */
+ { { GDK_Multi_key, GDK_asciitilde, GDK_U, 0 }, 0x0168 }, /* LATIN_CAPITAL_LETTER_U_WITH_TILDE */
+ { { GDK_Multi_key, GDK_asciitilde, GDK_a, 0 }, 0x00E3 }, /* LATIN_SMALL_LETTER_A_WITH_TILDE */
+ { { GDK_Multi_key, GDK_asciitilde, GDK_i, 0 }, 0x0129 }, /* LATIN_SMALL_LETTER_I_WITH_TILDE */
+ { { GDK_Multi_key, GDK_asciitilde, GDK_n, 0 }, 0x00F1 }, /* LATIN_SMALL_LETTER_N_WITH_TILDE */
+ { { GDK_Multi_key, GDK_asciitilde, GDK_o, 0 }, 0x00F5 }, /* LATIN_SMALL_LETTER_O_WITH_TILDE */
+ { { GDK_Multi_key, GDK_asciitilde, GDK_u, 0 }, 0x0169 }, /* LATIN_SMALL_LETTER_U_WITH_TILDE */
+ { { GDK_Multi_key, GDK_diaeresis, GDK_A, 0 }, 0x00C4 }, /* LATIN_CAPITAL_LETTER_A_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_diaeresis, GDK_E, 0 }, 0x00CB }, /* LATIN_CAPITAL_LETTER_E_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_diaeresis, GDK_I, 0 }, 0x00CF }, /* LATIN_CAPITAL_LETTER_I_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_diaeresis, GDK_O, 0 }, 0x00D6 }, /* LATIN_CAPITAL_LETTER_O_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_diaeresis, GDK_U, 0 }, 0x00DC }, /* LATIN_CAPITAL_LETTER_U_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_diaeresis, GDK_Y, 0 }, 0x0178 }, /* LATIN_CAPITAL_LETTER_Y_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_diaeresis, GDK_a, 0 }, 0x00E4 }, /* LATIN_SMALL_LETTER_A_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_diaeresis, GDK_e, 0 }, 0x00EB }, /* LATIN_SMALL_LETTER_E_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_diaeresis, GDK_i, 0 }, 0x00EF }, /* LATIN_SMALL_LETTER_I_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_diaeresis, GDK_o, 0 }, 0x00F6 }, /* LATIN_SMALL_LETTER_O_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_diaeresis, GDK_u, 0 }, 0x00FC }, /* LATIN_SMALL_LETTER_U_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_diaeresis, GDK_y, 0 }, 0x00FF }, /* LATIN_SMALL_LETTER_Y_WITH_DIAERESIS */
+ { { GDK_Multi_key, GDK_acute, GDK_A, 0 }, 0x00C1 }, /* LATIN_CAPITAL_LETTER_A_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_acute, GDK_E, 0 }, 0x00C9 }, /* LATIN_CAPITAL_LETTER_E_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_acute, GDK_I, 0 }, 0x00CD }, /* LATIN_CAPITAL_LETTER_I_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_acute, GDK_O, 0 }, 0x00D3 }, /* LATIN_CAPITAL_LETTER_O_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_acute, GDK_U, 0 }, 0x00DA }, /* LATIN_CAPITAL_LETTER_U_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_acute, GDK_Y, 0 }, 0x00DD }, /* LATIN_CAPITAL_LETTER_Y_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_acute, GDK_a, 0 }, 0x00E1 }, /* LATIN_SMALL_LETTER_A_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_acute, GDK_e, 0 }, 0x00E9 }, /* LATIN_SMALL_LETTER_E_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_acute, GDK_i, 0 }, 0x00ED }, /* LATIN_SMALL_LETTER_I_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_acute, GDK_o, 0 }, 0x00F3 }, /* LATIN_SMALL_LETTER_O_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_acute, GDK_u, 0 }, 0x00FA }, /* LATIN_SMALL_LETTER_U_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_acute, GDK_y, 0 }, 0x00FD }, /* LATIN_SMALL_LETTER_Y_WITH_ACUTE */
+ { { GDK_Multi_key, GDK_cedilla, GDK_S, 0 }, 0x015E }, /* LATIN_CAPITAL_LETTER_S_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_cedilla, GDK_s, 0 }, 0x015F }, /* LATIN_SMALL_LETTER_S_WITH_CEDILLA */
+ { { GDK_Multi_key, GDK_breve, GDK_G, 0 }, 0x011E }, /* LATIN_CAPITAL_LETTER_G_WITH_BREVE */
+ { { GDK_Multi_key, GDK_breve, GDK_g, 0 }, 0x011F }, /* LATIN_SMALL_LETTER_G_WITH_BREVE */
+};
+
+guint16 gtk_compose_ignore[] = {
+ GDK_Shift_L,
+ GDK_Shift_R,
+ GDK_Control_L,
+ GDK_Control_R,
+ GDK_Caps_Lock,
+ GDK_Shift_Lock,
+ GDK_Meta_L,
+ GDK_Meta_R,
+ GDK_Alt_L,
+ GDK_Alt_R,
+ GDK_Super_L,
+ GDK_Super_R,
+ GDK_Hyper_L,
+ GDK_Hyper_R
+};
+
+static void gtk_im_context_simple_class_init (GtkIMContextSimpleClass *class);
+static void gtk_im_context_simple_init (GtkIMContextSimple *im_context_simple);
+
+static gboolean gtk_im_context_simple_filter_keypress (GtkIMContext *context,
+ GdkEventKey *key);
+
+GtkType
+gtk_im_context_simple_get_type (void)
+{
+ static GtkType im_context_simple_type = 0;
+
+ if (!im_context_simple_type)
+ {
+ static const GtkTypeInfo im_context_simple_info =
+ {
+ "GtkIMContextSimple",
+ sizeof (GtkIMContextSimple),
+ sizeof (GtkIMContextSimpleClass),
+ (GtkClassInitFunc) gtk_im_context_simple_class_init,
+ (GtkObjectInitFunc) gtk_im_context_simple_init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL,
+ };
+
+ im_context_simple_type = gtk_type_unique (GTK_TYPE_IM_CONTEXT, &im_context_simple_info);
+ }
+
+ return im_context_simple_type;
+}
+
+static void
+gtk_im_context_simple_class_init (GtkIMContextSimpleClass *class)
+{
+ GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
+
+ im_context_class->filter_keypress = gtk_im_context_simple_filter_keypress;
+}
+
+static void
+gtk_im_context_simple_init (GtkIMContextSimple *im_context_simple)
+{
+}
+
+GtkIMContext *
+gtk_im_context_simple_new (void)
+{
+ return GTK_IM_CONTEXT (gtk_type_new (GTK_TYPE_IM_CONTEXT_SIMPLE));
+}
+
+/**
+ * unicode_guchar4_to_utf8:
+ * @ch: a ISO10646 character code
+ * @out: output buffer, must have at least 6 bytes of space.
+ *
+ * Convert a single character to utf8
+ *
+ * Return value: number of bytes written
+ **/
+static int
+ucs4_to_utf8 (unicode_char_t c, char *outbuf)
+{
+ size_t len = 0;
+ int first;
+ int i;
+
+ if (c < 0x80)
+ {
+ first = 0;
+ len = 1;
+ }
+ else if (c < 0x800)
+ {
+ first = 0xc0;
+ len = 2;
+ }
+ else if (c < 0x10000)
+ {
+ first = 0xe0;
+ len = 3;
+ }
+ else if (c < 0x200000)
+ {
+ first = 0xf0;
+ len = 4;
+ }
+ else if (c < 0x4000000)
+ {
+ first = 0xf8;
+ len = 5;
+ }
+ else
+ {
+ first = 0xfc;
+ len = 6;
+ }
+
+ for (i = len - 1; i > 0; --i)
+ {
+ outbuf[i] = (c & 0x3f) | 0x80;
+ c >>= 6;
+ }
+ outbuf[0] = c | first;
+
+ return len;
+}
+
+static void
+gtk_im_context_simple_commit_char (GtkIMContext *context,
+ unicode_char_t ch)
+{
+ gchar buf[7];
+ gint len;
+
+ len = ucs4_to_utf8 (ch, buf);
+ buf[len] = '\0';
+
+ gtk_signal_emit_by_name (GTK_OBJECT (context), "commit", &buf);
+}
+
+static int
+compare_seq (const void *key, const void *value)
+{
+ int i = 0;
+ const guint *keysyms = key;
+ const GtkComposeSeq *seq = value;
+
+ while (keysyms[i])
+ {
+ if (keysyms[i] < seq->keysyms[i])
+ return -1;
+ else if (keysyms[i] > seq->keysyms[i])
+ return 1;
+
+ i++;
+ }
+
+ return 0;
+}
+
+static gboolean
+gtk_im_context_simple_filter_keypress (GtkIMContext *context,
+ GdkEventKey *event)
+{
+ GtkIMContextSimple *context_simple = GTK_IM_CONTEXT_SIMPLE (context);
+ GtkComposeSeq *seq;
+
+ unicode_char_t ch;
+ int n_compose = 0;
+ int i;
+
+ /* Ignore modifier key presses
+ */
+ for (i=0; i < G_N_ELEMENTS (gtk_compose_ignore); i++)
+ if (event->keyval == gtk_compose_ignore[i])
+ return FALSE;
+
+ /* Then, check for compose sequences
+ */
+ while (context_simple->compose_buffer[n_compose] != 0)
+ n_compose++;
+
+ context_simple->compose_buffer[n_compose++] = event->keyval;
+ context_simple->compose_buffer[n_compose] = 0;
+
+ seq = bsearch (context_simple->compose_buffer,
+ gtk_compose_seqs, G_N_ELEMENTS (gtk_compose_seqs),
+ sizeof (GtkComposeSeq), compare_seq);
+
+ if (seq)
+ {
+ if (n_compose == GTK_MAX_COMPOSE_LEN ||
+ seq->keysyms[n_compose] == 0) /* complete sequence */
+ {
+ gtk_im_context_simple_commit_char (context, seq->unicode);
+ context_simple->compose_buffer[0] = 0;
+ }
+
+ return TRUE;
+ }
+
+ /* No compose sequences found, try simple conversion to unicode
+ */
+ context_simple->compose_buffer[0] = 0;
+ if (n_compose > 1) /* Invalid sequence */
+ {
+ gdk_beep();
+ return TRUE;
+ }
+
+ ch = gdk_keyval_to_unicode (event->keyval);
+ if (ch != 0)
+ {
+ gtk_im_context_simple_commit_char (context, ch);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
diff --git a/gtk/gtkimcontextsimple.h b/gtk/gtkimcontextsimple.h
new file mode 100644
index 000000000..87e374169
--- /dev/null
+++ b/gtk/gtkimcontextsimple.h
@@ -0,0 +1,63 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2000 Red Hat Software
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_IM_CONTEXT_SIMPLE_H__
+#define __GTK_IM_CONTEXT_SIMPLE_H__
+
+#include <gtk/gtkimcontext.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GTK_TYPE_IM_CONTEXT_SIMPLE (gtk_im_context_simple_get_type ())
+#define GTK_IM_CONTEXT_SIMPLE(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_IM_CONTEXT_SIMPLE, GtkIMContextSimple))
+#define GTK_IM_CONTEXT_SIMPLE_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_IM_CONTEXT_SIMPLE, GtkIMContextSimpleClass))
+#define GTK_IS_IM_CONTEXT_SIMPLE(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_IM_CONTEXT_SIMPLE))
+#define GTK_IS_IM_CONTEXT_SIMPLE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_IM_CONTEXT_SIMPLE))
+#define GTK_IM_CONTEXT_SIMPLE_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_IM_CONTEXT_SIMPLE, GtkIMContextSimpleClass))
+
+
+typedef struct _GtkIMContextSimple GtkIMContextSimple;
+typedef struct _GtkIMContextSimpleClass GtkIMContextSimpleClass;
+
+#define GTK_MAX_COMPOSE_LEN 4
+
+struct _GtkIMContextSimple
+{
+ GtkIMContext object;
+
+ guint compose_buffer[GTK_MAX_COMPOSE_LEN + 1];
+};
+
+struct _GtkIMContextSimpleClass
+{
+ GtkIMContextClass parent_class;
+};
+
+GtkType gtk_im_context_simple_get_type (void);
+GtkIMContext *gtk_im_context_simple_new (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_IM_CONTEXT_SIMPLE_H__ */
diff --git a/gtk/gtkimmulticontext.c b/gtk/gtkimmulticontext.c
new file mode 100644
index 000000000..ddfd18e67
--- /dev/null
+++ b/gtk/gtkimmulticontext.c
@@ -0,0 +1,247 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2000 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gtksignal.h"
+#include "gtkimmulticontext.h"
+#include "gtkimcontextsimple.h"
+
+static void gtk_im_multicontext_class_init (GtkIMMulticontextClass *class);
+static void gtk_im_multicontext_init (GtkIMMulticontext *im_multicontext);
+static void gtk_im_multicontext_finalize (GObject *object);
+
+static void gtk_im_multicontext_set_slave (GtkIMMulticontext *multicontext,
+ GtkIMContext *slave);
+
+static void gtk_im_multicontext_set_client_window (GtkIMContext *context,
+ GdkWindow *window);
+static void gtk_im_multicontext_get_preedit_string (GtkIMContext *context,
+ gchar **str,
+ PangoAttrList **attrs);
+static gboolean gtk_im_multicontext_filter_keypress (GtkIMContext *context,
+ GdkEventKey *event);
+static void gtk_im_multicontext_focus_in (GtkIMContext *context);
+static void gtk_im_multicontext_focus_out (GtkIMContext *context);
+
+void gtk_im_multicontext_preedit_start_cb (GtkIMContext *slave,
+ GtkIMMulticontext *multicontext);
+void gtk_im_multicontext_preedit_end_cb (GtkIMContext *slave,
+ GtkIMMulticontext *multicontext);
+void gtk_im_multicontext_preedit_changed_cb (GtkIMContext *slave,
+ GtkIMMulticontext *multicontext);
+void gtk_im_multicontext_commit_cb (GtkIMContext *slave,
+ const gchar *str,
+ GtkIMMulticontext *multicontext);
+
+static GtkIMContextClass *parent_class;
+
+GtkType
+gtk_im_multicontext_get_type (void)
+{
+ static GtkType im_multicontext_type = 0;
+
+ if (!im_multicontext_type)
+ {
+ static const GtkTypeInfo im_multicontext_info =
+ {
+ "GtkIMMulticontext",
+ sizeof (GtkIMMulticontext),
+ sizeof (GtkIMMulticontextClass),
+ (GtkClassInitFunc) gtk_im_multicontext_class_init,
+ (GtkObjectInitFunc) gtk_im_multicontext_init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL,
+ };
+
+ im_multicontext_type = gtk_type_unique (GTK_TYPE_IM_CONTEXT, &im_multicontext_info);
+ }
+
+ return im_multicontext_type;
+}
+
+static void
+gtk_im_multicontext_class_init (GtkIMMulticontextClass *class)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (class);
+ GtkIMContextClass *im_context_class = GTK_IM_CONTEXT_CLASS (class);
+
+ parent_class = g_type_class_peek_parent (class);
+
+ im_context_class->set_client_window = gtk_im_multicontext_set_client_window;
+ im_context_class->get_preedit_string = gtk_im_multicontext_get_preedit_string;
+ im_context_class->filter_keypress = gtk_im_multicontext_filter_keypress;
+ im_context_class->focus_in = gtk_im_multicontext_focus_in;
+ im_context_class->focus_out = gtk_im_multicontext_focus_out;
+
+ gobject_class->finalize = gtk_im_multicontext_finalize;
+}
+
+static void
+gtk_im_multicontext_init (GtkIMMulticontext *multicontext)
+{
+ multicontext->slave = NULL;
+}
+
+GtkIMContext *
+gtk_im_multicontext_new (void)
+{
+ return GTK_IM_CONTEXT (gtk_type_new (GTK_TYPE_IM_MULTICONTEXT));
+}
+
+static void
+gtk_im_multicontext_finalize (GObject *object)
+{
+ gtk_im_multicontext_set_slave (GTK_IM_MULTICONTEXT (object), NULL);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gtk_im_multicontext_set_slave (GtkIMMulticontext *multicontext,
+ GtkIMContext *slave)
+{
+ if (multicontext->slave)
+ {
+ gtk_signal_disconnect_by_data (GTK_OBJECT (multicontext->slave), multicontext);
+ gtk_object_unref (GTK_OBJECT (multicontext->slave));
+ }
+
+ multicontext->slave = slave;
+
+ if (multicontext->slave)
+ {
+ gtk_object_ref (GTK_OBJECT (multicontext->slave));
+ gtk_object_sink (GTK_OBJECT (multicontext->slave));
+
+ gtk_signal_connect (GTK_OBJECT (multicontext->slave), "preedit_start",
+ GTK_SIGNAL_FUNC (gtk_im_multicontext_preedit_start_cb),
+ multicontext);
+ gtk_signal_connect (GTK_OBJECT (multicontext->slave), "preedit_end",
+ GTK_SIGNAL_FUNC (gtk_im_multicontext_preedit_end_cb),
+ multicontext);
+ gtk_signal_connect (GTK_OBJECT (multicontext->slave), "preedit_changed",
+ GTK_SIGNAL_FUNC (gtk_im_multicontext_preedit_changed_cb),
+ multicontext);
+ gtk_signal_connect (GTK_OBJECT (multicontext->slave), "commit",
+ GTK_SIGNAL_FUNC (gtk_im_multicontext_commit_cb),
+ multicontext);
+ }
+}
+
+static GtkIMContext *
+gtk_im_multicontext_get_slave (GtkIMMulticontext *multicontext)
+{
+ if (!multicontext->slave)
+ gtk_im_multicontext_set_slave (multicontext, gtk_im_context_simple_new ());
+
+ return multicontext->slave;
+}
+
+static void
+gtk_im_multicontext_set_client_window (GtkIMContext *context,
+ GdkWindow *window)
+{
+ GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
+ GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
+
+ if (slave)
+ gtk_im_context_set_client_window (slave, window);
+}
+
+static void
+gtk_im_multicontext_get_preedit_string (GtkIMContext *context,
+ gchar **str,
+ PangoAttrList **attrs)
+{
+ GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
+ GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
+
+ if (slave)
+ gtk_im_context_get_preedit_string (slave, str, attrs);
+ else
+ {
+ if (str)
+ *str = g_strdup ("");
+ if (attrs)
+ *attrs = pango_attr_list_new ();
+ }
+}
+
+static gboolean
+gtk_im_multicontext_filter_keypress (GtkIMContext *context,
+ GdkEventKey *event)
+{
+ GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
+ GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
+
+ if (slave)
+ return gtk_im_context_filter_keypress (slave, event);
+ else
+ return FALSE;
+}
+
+static void
+gtk_im_multicontext_focus_in (GtkIMContext *context)
+{
+ GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
+ GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
+
+ if (slave)
+ return gtk_im_context_focus_in (slave);
+}
+
+static void
+gtk_im_multicontext_focus_out (GtkIMContext *context)
+{
+ GtkIMMulticontext *multicontext = GTK_IM_MULTICONTEXT (context);
+ GtkIMContext *slave = gtk_im_multicontext_get_slave (multicontext);
+
+ if (slave)
+ return gtk_im_context_focus_out (slave);
+}
+
+void
+gtk_im_multicontext_preedit_start_cb (GtkIMContext *slave,
+ GtkIMMulticontext *multicontext)
+{
+ gtk_signal_emit_by_name (GTK_OBJECT (multicontext), "preedit_start");
+}
+
+void
+gtk_im_multicontext_preedit_end_cb (GtkIMContext *slave,
+ GtkIMMulticontext *multicontext)
+{
+ gtk_signal_emit_by_name (GTK_OBJECT (multicontext), "preedit_end");
+}
+
+void
+gtk_im_multicontext_preedit_changed_cb (GtkIMContext *slave,
+ GtkIMMulticontext *multicontext)
+{
+ gtk_signal_emit_by_name (GTK_OBJECT (multicontext), "preedit_changed");
+}
+
+void
+gtk_im_multicontext_commit_cb (GtkIMContext *slave,
+ const gchar *str,
+ GtkIMMulticontext *multicontext)
+{
+ gtk_signal_emit_by_name (GTK_OBJECT (multicontext), "commit", str);;
+}
+
diff --git a/gtk/gtkimmulticontext.h b/gtk/gtkimmulticontext.h
new file mode 100644
index 000000000..f242c71bc
--- /dev/null
+++ b/gtk/gtkimmulticontext.h
@@ -0,0 +1,61 @@
+/* GTK - The GIMP Toolkit
+ * Copyright (C) 2000 Red Hat Software
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GTK_IM_MULTICONTEXT_H__
+#define __GTK_IM_MULTICONTEXT_H__
+
+#include <gtk/gtkimcontext.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+
+#define GTK_TYPE_IM_MULTICONTEXT (gtk_im_multicontext_get_type ())
+#define GTK_IM_MULTICONTEXT(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_IM_MULTICONTEXT, GtkIMMulticontext))
+#define GTK_IM_MULTICONTEXT_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_IM_MULTICONTEXT, GtkIMMulticontextClass))
+#define GTK_IS_IM_MULTICONTEXT(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_IM_MULTICONTEXT))
+#define GTK_IS_IM_MULTICONTEXT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_IM_MULTICONTEXT))
+#define GTK_IM_MULTICONTEXT_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_IM_MULTICONTEXT, GtkIMMulticontextClass))
+
+
+typedef struct _GtkIMMulticontext GtkIMMulticontext;
+typedef struct _GtkIMMulticontextClass GtkIMMulticontextClass;
+
+struct _GtkIMMulticontext
+{
+ GtkIMContext object;
+
+ GtkIMContext *slave;
+};
+
+struct _GtkIMMulticontextClass
+{
+ GtkIMContextClass parent_class;
+};
+
+GtkType gtk_im_multicontext_get_type (void);
+GtkIMContext *gtk_im_multicontext_new (void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif /* __GTK_IM_MULTICONTEXT_H__ */
diff --git a/gtk/gtkinvisible.c b/gtk/gtkinvisible.c
index 36939c879..f050630e6 100644
--- a/gtk/gtkinvisible.c
+++ b/gtk/gtkinvisible.c
@@ -29,6 +29,7 @@
static void gtk_invisible_class_init (GtkInvisibleClass *klass);
static void gtk_invisible_init (GtkInvisible *invisible);
+static void gtk_invisible_destroy (GtkObject *object);
static void gtk_invisible_realize (GtkWidget *widget);
static void gtk_invisible_show (GtkWidget *widget);
static void gtk_invisible_size_allocate (GtkWidget *widget,
@@ -62,13 +63,17 @@ gtk_invisible_get_type (void)
static void
gtk_invisible_class_init (GtkInvisibleClass *class)
{
+ GtkObjectClass *object_class;
GtkWidgetClass *widget_class;
widget_class = (GtkWidgetClass*) class;
+ object_class = (GtkObjectClass*) class;
widget_class->realize = gtk_invisible_realize;
widget_class->show = gtk_invisible_show;
widget_class->size_allocate = gtk_invisible_size_allocate;
+
+ object_class->destroy = gtk_invisible_destroy;
}
static void
@@ -78,6 +83,20 @@ gtk_invisible_init (GtkInvisible *invisible)
gtk_widget_ref (GTK_WIDGET (invisible));
gtk_object_sink (GTK_OBJECT (invisible));
+
+ invisible->has_user_ref_count = TRUE;
+}
+
+static void
+gtk_invisible_destroy (GtkObject *object)
+{
+ GtkInvisible *invisible = GTK_INVISIBLE (object);
+
+ if (invisible->has_user_ref_count)
+ {
+ invisible->has_user_ref_count = FALSE;
+ gtk_widget_unref (GTK_WIDGET (invisible));
+ }
}
GtkWidget*
diff --git a/gtk/gtkinvisible.h b/gtk/gtkinvisible.h
index 0bddc8788..b68d28416 100644
--- a/gtk/gtkinvisible.h
+++ b/gtk/gtkinvisible.h
@@ -51,6 +51,7 @@ typedef struct _GtkInvisibleClass GtkInvisibleClass;
struct _GtkInvisible
{
GtkWidget widget;
+ gboolean has_user_ref_count;
};
struct _GtkInvisibleClass
diff --git a/gtk/gtklabel.c b/gtk/gtklabel.c
index d6b64572c..6e788dcf6 100644
--- a/gtk/gtklabel.c
+++ b/gtk/gtklabel.c
@@ -28,6 +28,9 @@
#include "gtklabel.h"
#include "gdk/gdkkeysyms.h"
#include "gdk/gdki18n.h"
+#include <pango/pango.h>
+#include <unicode.h>
+
enum {
ARG_0,
@@ -37,35 +40,6 @@ enum {
ARG_WRAP
};
-typedef struct _GtkLabelULine GtkLabelULine;
-struct _GtkLabelWord
-{
- GdkWChar *beginning;
- gint length;
-
- /* FIXME:
- * We need (space,width) only before we've set (x,y), so to save
- * memory, these pairs should really be wrapped in a union.
- * I haven't yet figured out how to do this without making the code
- * look ugly.
- */
- gint space;
- gint width;
- gint x;
- gint y;
- GtkLabelWord *next;
- gint uline_y;
- GtkLabelULine *uline;
-};
-
-struct _GtkLabelULine
-{
- gint x1;
- gint x2;
- gint y;
- GtkLabelULine *next;
-};
-
static void gtk_label_class_init (GtkLabelClass *klass);
static void gtk_label_init (GtkLabel *label);
static void gtk_label_set_arg (GtkObject *object,
@@ -82,18 +56,9 @@ static void gtk_label_style_set (GtkWidget *widget,
static gint gtk_label_expose (GtkWidget *widget,
GdkEventExpose *event);
-static GtkLabelWord* gtk_label_word_alloc (void);
-static GtkLabelULine* gtk_label_uline_alloc (void);
-static void gtk_label_free_words (GtkLabel *label);
-static void gtk_label_free_ulines (GtkLabelWord *word);
-static gint gtk_label_split_text (GtkLabel *label);
-
static GtkMiscClass *parent_class = NULL;
-static GMemChunk *word_chunk = NULL;
-static GMemChunk *uline_chunk = NULL;
-
GtkType
gtk_label_get_type (void)
{
@@ -210,14 +175,13 @@ gtk_label_init (GtkLabel *label)
GTK_WIDGET_SET_FLAGS (label, GTK_NO_WINDOW);
label->label = NULL;
- label->label_wc = NULL;
label->pattern = NULL;
- label->words = NULL;
-
- label->max_width = 0;
label->jtype = GTK_JUSTIFY_CENTER;
label->wrap = FALSE;
+
+ label->layout = NULL;
+ label->rtl = (gtk_widget_get_direction (GTK_WIDGET (label)) == GTK_TEXT_DIR_RTL);
gtk_label_set_text (label, "");
}
@@ -237,16 +201,13 @@ gtk_label_new (const gchar *str)
static inline void
gtk_label_set_text_internal (GtkLabel *label,
- gchar *str,
- GdkWChar *str_wc)
+ gchar *str)
{
- gtk_label_free_words (label);
-
g_free (label->label);
- g_free (label->label_wc);
-
+
label->label = str;
- label->label_wc = str_wc;
+ if (label->layout)
+ pango_layout_set_text (label->layout, str, -1);
gtk_widget_queue_resize (GTK_WIDGET (label));
}
@@ -255,28 +216,27 @@ void
gtk_label_set_text (GtkLabel *label,
const gchar *str)
{
- GdkWChar *str_wc;
- gint len;
- gint wc_len;
-
g_return_if_fail (GTK_IS_LABEL (label));
- if (!str)
- str = "";
- if (!label->label || strcmp (label->label, str))
- {
- /* Convert text to wide characters */
- len = strlen (str);
- str_wc = g_new (GdkWChar, len + 1);
- wc_len = gdk_mbstowcs (str_wc, str, len + 1);
- if (wc_len >= 0)
- {
- str_wc[wc_len] = '\0';
- gtk_label_set_text_internal (label, g_strdup (str), str_wc);
- }
- else
- g_free (str_wc);
- }
+ gtk_label_set_text_internal (label, g_strdup (str ? str : ""));
+}
+
+/**
+ * gtk_label_get_text:
+ * @label: a #GtkLabel
+ *
+ * Fetches the text from a label widget
+ *
+ * Return value: the text in the label widget. This value must
+ * be freed with g_free().
+ **/
+gchar *
+gtk_label_get_text (GtkLabel *label)
+{
+ g_return_if_fail (label != NULL);
+ g_return_if_fail (GTK_IS_LABEL (label));
+
+ return g_strdup (label->label);
}
void
@@ -285,8 +245,6 @@ gtk_label_set_pattern (GtkLabel *label,
{
g_return_if_fail (GTK_IS_LABEL (label));
- gtk_label_free_words (label);
-
g_free (label->pattern);
label->pattern = g_strdup (pattern);
@@ -302,10 +260,15 @@ gtk_label_set_justify (GtkLabel *label,
if ((GtkJustification) label->jtype != jtype)
{
- gtk_label_free_words (label);
-
label->jtype = jtype;
+ if (label->layout)
+ {
+ /* No real need to be this drastic, but easier than duplicating the code */
+ pango_layout_unref (label->layout);
+ label->layout = NULL;
+ }
+
gtk_widget_queue_resize (GTK_WIDGET (label));
}
}
@@ -320,8 +283,6 @@ gtk_label_set_line_wrap (GtkLabel *label,
if (label->wrap != wrap)
{
- gtk_label_free_words (label);
-
label->wrap = wrap;
gtk_widget_queue_resize (GTK_WIDGET (label));
@@ -339,15 +300,6 @@ gtk_label_get (GtkLabel *label,
*str = label->label;
}
-gchar *
-gtk_label_get_text (GtkLabel *label)
-{
- g_return_val_if_fail (label != NULL, NULL);
- g_return_val_if_fail (GTK_IS_LABEL (label), NULL);
-
- return g_strdup (label->label);
-}
-
static void
gtk_label_finalize (GObject *object)
{
@@ -358,427 +310,53 @@ gtk_label_finalize (GObject *object)
label = GTK_LABEL (object);
g_free (label->label);
- g_free (label->label_wc);
g_free (label->pattern);
- gtk_label_free_words (label);
+ if (label->layout)
+ pango_layout_unref (label->layout);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
-static GtkLabelULine*
-gtk_label_uline_alloc (void)
+static PangoAttrList *
+gtk_label_pattern_to_attrs (GtkLabel *label)
{
- GtkLabelULine *uline;
-
- if (!uline_chunk)
- uline_chunk = g_mem_chunk_create (GtkLabelWord, 32, G_ALLOC_AND_FREE);
+ PangoAttrList *attrs = pango_attr_list_new ();
- uline = g_chunk_new0 (GtkLabelULine, uline_chunk);
-
- uline->next = NULL;
-
- return uline;
-}
-
-static void
-gtk_label_free_ulines (GtkLabelWord *word)
-{
- while (word->uline)
+ if (label->pattern)
{
- GtkLabelULine *uline = word->uline;
+ const char *start;
+ const char *p = label->label;
+ const char *q = label->pattern;
- word->uline = uline->next;
- g_chunk_free (uline, uline_chunk);
- }
-}
-
-static GtkLabelWord*
-gtk_label_word_alloc (void)
-{
- GtkLabelWord * word;
-
- if (!word_chunk)
- word_chunk = g_mem_chunk_create (GtkLabelWord, 32, G_ALLOC_AND_FREE);
-
- word = g_chunk_new0 (GtkLabelWord, word_chunk);
-
- word->beginning = NULL;
- word->next = NULL;
- word->uline = NULL;
-
- return word;
-}
-
-static void
-gtk_label_free_words (GtkLabel *label)
-{
- while (label->words)
- {
- GtkLabelWord *word = label->words;
-
- label->words = word->next;
-
- gtk_label_free_ulines (word);
-
- g_chunk_free (word, word_chunk);
- }
-}
-
-static gint
-gtk_label_split_text (GtkLabel *label)
-{
- GtkLabelWord *word, **tailp;
- gint space_width, line_width, max_line_width;
- GdkWChar *str, *p;
-
- gtk_label_free_words (label);
- if (label->label == NULL)
- return 0;
-
- /* Split text at new-lines. */
- space_width = gdk_string_width (GTK_WIDGET (label)->style->font, " ");
-
- line_width = 0;
- max_line_width = 0;
- tailp = &label->words;
- str = label->label_wc;
-
- while (*str)
- {
- word = gtk_label_word_alloc ();
-
- if (str == label->label_wc || str[-1] == '\n')
- {
- /* Paragraph break */
- word->space = 0;
-
- max_line_width = MAX (line_width, max_line_width);
- line_width = 0;
- }
- else if (str[0] == ' ')
+ while (1)
{
- while (str[0] == ' ')
+ while (*p && *q && *q != '_')
{
- str++;
- word->space += space_width;
+ p = unicode_next_utf8 (p);
+ q++;
}
- }
- else
- {
- /* Regular inter-word space */
- word->space = space_width;
- }
-
- word->beginning = str;
-
- word->length = 0;
- p = word->beginning;
- while (*p && *p != '\n')
- {
- word->length++;
- p++;
- }
-
- word->width = gdk_text_width_wc (GTK_WIDGET (label)->style->font, str, word->length);
-
- str += word->length;
- if (*str)
- str++;
-
- line_width += word->space + word->width;
-
- *tailp = word;
- tailp = &word->next;
- }
-
- /* Add an empty word to represent an empty line
- */
- if (str == label->label_wc || str[-1] == '\n')
- {
- word = gtk_label_word_alloc ();
-
- word->space = 0;
- word->beginning = str;
- word->length = 0;
- word->width = 0;
-
- *tailp = word;
- tailp = &word->next;
- }
-
- return MAX (line_width, max_line_width);
-}
-
-/* this needs to handle white space better. */
-static gint
-gtk_label_split_text_wrapped (GtkLabel *label)
-{
- GtkLabelWord *word, **tailp;
- gint space_width, line_width, max_line_width;
- GdkWChar *str, *p;
-
- gtk_label_free_words (label);
- if (label->label == NULL)
- return 0;
-
- /* Split text at new-lines. (Or at spaces in the case of paragraphs). */
- space_width = gdk_string_width (GTK_WIDGET (label)->style->font, " ");
-
- line_width = 0;
- max_line_width = 0;
- tailp = &label->words;
- str = label->label_wc;
- while (*str)
- {
- word = gtk_label_word_alloc ();
-
- if (str == label->label_wc || str[-1] == '\n')
- {
- /* Paragraph break */
- word->space = 0;
-
- max_line_width = MAX (line_width, max_line_width);
- line_width = 0;
- }
- else if (str[0] == ' ')
- {
- gint nspaces = 0;
-
- while (str[0] == ' ')
+ start = p;
+ while (*p && *q && *q == '_')
{
- nspaces++;
- str++;
+ p = unicode_next_utf8 (p);
+ q++;
}
-
- if (label->jtype == GTK_JUSTIFY_FILL)
- word->space = (space_width * 3 + 1) / 2;
- else
- word->space = space_width * nspaces;
- }
- else
- {
- /* Regular inter-word space */
- word->space = space_width;
- }
-
- word->beginning = str;
- word->length = 0;
- p = word->beginning;
- while (*p && !gdk_iswspace (*p))
- {
- word->length++;
- p++;
- }
- word->width = gdk_text_width_wc (GTK_WIDGET (label)->style->font, str, word->length);
-
- str += word->length;
- if (*str)
- str++;
-
- line_width += word->space + word->width;
-
- *tailp = word;
- tailp = &word->next;
- }
-
- return MAX (line_width, max_line_width);
-}
-
-/* gtk_label_pick_width
- *
- * Split paragraphs, trying to make each line at least min_width,
- * and trying even harder to make each line no longer than max_width.
- *
- * Returns the length of the longest resulting line.
- *
- * (The reason we go to all this effort to pick a paragraph width is to
- * try to avoid the lame look of a short paragraph with a
- * short final line.)
- */
-static gint
-gtk_label_pick_width (GtkLabel *label,
- gint min_width,
- gint max_width)
-{
- GtkLabelWord *word;
- gint width, line_width;
-
- g_return_val_if_fail (label->wrap, min_width);
-
- line_width = 0;
- width = 0;
- for (word = label->words; word; word = word->next)
- {
- if (word->space == 0
- || (line_width
- && (line_width >= min_width
- || line_width + word->width + word->space > max_width)))
- {
- /* New line */
- width = MAX (width, line_width);
- line_width = 0;
- }
- line_width += word->space + word->width;
- }
-
- return MAX (width, line_width);
-}
-/* Here, we finalize the lines.
- * This is only for non-wrap labels. Wrapped labels
- * use gtk_label_finalize_wrap instead.
- */
-static void
-gtk_label_finalize_lines (GtkLabel *label,
- GtkRequisition *requisition,
- gint max_line_width)
-{
- GtkLabelWord *line;
- gint y, baseline_skip, y_max;
- gint i, j;
- gchar *ptrn;
-
- g_return_if_fail (!label->wrap);
- ptrn = label->pattern;
-
- y = 0;
- baseline_skip = (GTK_WIDGET (label)->style->font->ascent +
- GTK_WIDGET (label)->style->font->descent + 2);
-
- for (line = label->words; line; line = line->next)
- {
- if (label->jtype == GTK_JUSTIFY_CENTER)
- line->x = (max_line_width - line->width) / 2;
- else if (label->jtype == GTK_JUSTIFY_RIGHT)
- line->x = max_line_width - line->width;
- else
- line->x = 0;
-
- line->y = y + GTK_WIDGET (label)->style->font->ascent + 1;
- y_max = 0;
-
- /* now we deal with the underline stuff; */
- if (ptrn && ptrn[0] != '\0')
- {
- for (i = 0; i < line->length; i++)
+ if (p > start)
{
- if (ptrn[i] == '\0')
- break;
- else if (ptrn[i] == '_')
- {
- gint descent;
- gint rbearing;
- gint lbearing;
- gint width;
- gint offset;
- GtkLabelULine *uline;
-
- for (j = i + 1; j < line->length; j++)
- {
- if (ptrn[j] == '\0')
- break;
- else if (ptrn[j] == ' ')
- break;
- }
-
- /* good. Now we have an underlined segment.
- * let's measure it and record it.
- */
- offset = gdk_text_width_wc (GTK_WIDGET (label)->style->font,
- line->beginning,
- i);
- gdk_text_extents_wc (GTK_WIDGET (label)->style->font,
- line->beginning+i,
- j-i, &lbearing,
- &rbearing, &width, NULL,
- &descent);
- y_max = MAX (descent + 2, y_max);
- uline = gtk_label_uline_alloc ();
- uline->x1 = offset + line->x + lbearing - 1;
- uline->x2 = offset + line->x + rbearing;
- uline->y = line->y + descent + 2;
- uline->next = line->uline;
- line->uline = uline;
- i = j - 1;
- }
+ PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_LOW);
+ attr->start_index = start - label->label;
+ attr->end_index = p - label->label;
+
+ pango_attr_list_insert (attrs, attr);
}
- if (strlen (ptrn) > line->length)
- /* the + 1 is for line breaks. */
- ptrn += line->length + 1;
else
- ptrn = NULL;
- }
- y += (baseline_skip + y_max);
- }
-
- label->max_width = max_line_width;
- requisition->width = max_line_width + 2 * label->misc.xpad;
- requisition->height = y + 2 * label->misc.ypad;
-}
-
-/* this finalizes word-wrapped words */
-static void
-gtk_label_finalize_lines_wrap (GtkLabel *label,
- GtkRequisition *requisition,
- gint max_line_width)
-{
- GtkLabelWord *word, *line, *next_line;
- GtkWidget *widget;
- gchar *ptrn;
- gint x, y, space, extra_width, add_space, baseline_skip;
-
- g_return_if_fail (label->wrap);
-
- ptrn = label->pattern;
- y = 0;
- baseline_skip = (GTK_WIDGET (label)->style->font->ascent +
- GTK_WIDGET (label)->style->font->descent + 1);
-
- for (line = label->words; line != 0; line = next_line)
- {
- space = 0;
- extra_width = max_line_width - line->width;
-
- for (next_line = line->next; next_line; next_line = next_line->next)
- {
- if (next_line->space == 0)
- break; /* New paragraph */
- if (next_line->space + next_line->width > extra_width)
break;
- extra_width -= next_line->space + next_line->width;
- space += next_line->space;
- }
-
- line->x = 0;
- line->y = y + GTK_WIDGET (label)->style->font->ascent + 1;
- x = line->width;
- add_space = 0;
-
- for (word = line->next; word != next_line; word = word->next)
- {
- if (next_line && next_line->space)
- {
- /* Not last line of paragraph --- fill line if needed */
- if (label->jtype == GTK_JUSTIFY_FILL) {
- add_space = (extra_width * word->space + space / 2) / space;
- extra_width -= add_space;
- space -= word->space;
- }
- }
-
- word->x = x + word->space + add_space;
- word->y = line->y;
- x = word->x + word->width;
}
-
- y += (baseline_skip);
}
-
- label->max_width = max_line_width;
- widget = GTK_WIDGET (label);
- requisition->width = max_line_width + 2 * label->misc.xpad;
- requisition->height = y + 2 * label->misc.ypad + 1;
+
+ return attrs;
}
static void
@@ -786,6 +364,7 @@ gtk_label_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
GtkLabel *label;
+ PangoRectangle logical_rect;
g_return_if_fail (GTK_IS_LABEL (widget));
g_return_if_fail (requisition != NULL);
@@ -812,43 +391,146 @@ gtk_label_size_request (GtkWidget *widget,
* Too much of a pain to detect all these case, so always re-fill. I
* don't think it's really that slow.
*/
+
+ requisition->width = label->misc.xpad;
+ requisition->height = label->misc.ypad;
+
+ /* Detect direction changes. FIXME: make this a signal
+ */
+ if (label->rtl != (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) &&
+ label->layout)
+ {
+ label->rtl = !label->rtl;
+ pango_layout_unref (label->layout);
+ label->layout = NULL;
+ }
+ if (!label->layout)
+ {
+ PangoAlignment align = PANGO_ALIGN_LEFT; /* Quiet gcc */
+ PangoAttrList *attrs = gtk_label_pattern_to_attrs (label);
+
+ label->layout = gtk_widget_create_pango_layout (widget);
+ pango_layout_set_attributes (label->layout, attrs);
+ pango_attr_list_unref (attrs);
+
+ pango_layout_set_text (label->layout, label->label, -1);
+
+ switch (label->jtype)
+ {
+ case GTK_JUSTIFY_LEFT:
+ align = PANGO_ALIGN_LEFT;
+ break;
+ case GTK_JUSTIFY_RIGHT:
+ align = PANGO_ALIGN_RIGHT;
+ break;
+ case GTK_JUSTIFY_CENTER:
+ align = PANGO_ALIGN_LEFT;
+ break;
+ case GTK_JUSTIFY_FILL:
+ /* FIXME: This just doesn't work to do this */
+ align = PANGO_ALIGN_LEFT;
+ pango_layout_set_justify (label->layout, TRUE);
+ break;
+ default:
+ g_assert_not_reached();
+ }
+
+ pango_layout_set_alignment (label->layout, align);
+ }
+
if (label->wrap)
{
GtkWidgetAuxInfo *aux_info;
gint longest_paragraph;
-
- longest_paragraph = gtk_label_split_text_wrapped (label);
-
+ gint width, height;
+ gint real_width;
+
aux_info = gtk_object_get_data (GTK_OBJECT (widget), "gtk-aux-info");
if (aux_info && aux_info->width > 0)
{
- label->max_width = MAX (aux_info->width - 2 * label->misc.xpad, 1);
- gtk_label_split_text_wrapped (label);
+ pango_layout_set_width (label->layout, aux_info->width * PANGO_SCALE);
+ pango_layout_get_extents (label->layout, NULL, &logical_rect);
+
+ requisition->width += aux_info->width;
+ requisition->height += logical_rect.height / PANGO_SCALE;
}
else
{
- label->max_width = gdk_string_width (GTK_WIDGET (label)->style->font,
- "This is a good enough length for any line to have.");
- label->max_width = MIN (label->max_width, (gdk_screen_width () + 1) / 2);
- label->max_width = MIN (label->max_width, longest_paragraph);
+ pango_layout_set_width (label->layout, -1);
+ pango_layout_get_extents (label->layout, NULL, &logical_rect);
+
+ width = logical_rect.width;
+ height = logical_rect.height;
+
+ /* Try to guess a reasonable maximum width
+ */
+ longest_paragraph = width;
+
+ width = MIN (width,
+ PANGO_SCALE * gdk_string_width (GTK_WIDGET (label)->style->font,
+ "This long string gives a good enough length for any line to have."));
+ width = MIN (width,
+ PANGO_SCALE * (gdk_screen_width () + 1) / 2);
+
+ pango_layout_set_width (label->layout, width);
+ pango_layout_get_extents (label->layout, NULL, &logical_rect);
+ real_width = logical_rect.width;
+ height = logical_rect.height;
+
+ /* Unfortunately, the above may leave us with a very unbalanced looking paragraph,
+ * so we try short search for a narrower width that leaves us with the same height
+ */
if (longest_paragraph > 0)
{
gint nlines, perfect_width;
-
- nlines = (longest_paragraph + label->max_width - 1) / label->max_width;
+
+ nlines = pango_layout_get_line_count (label->layout);
perfect_width = (longest_paragraph + nlines - 1) / nlines;
- label->max_width = gtk_label_pick_width (label,
- perfect_width,
- label->max_width);
+
+ if (perfect_width < width)
+ {
+ pango_layout_set_width (label->layout, perfect_width);
+ pango_layout_get_extents (label->layout, NULL, &logical_rect);
+
+ if (logical_rect.height <= height)
+ {
+ width = perfect_width;
+ real_width = logical_rect.width;
+ height = logical_rect.height;
+ }
+ else
+ {
+ gint mid_width = (perfect_width + width) / 2;
+
+ if (mid_width > perfect_width)
+ {
+ pango_layout_set_width (label->layout, mid_width);
+ pango_layout_get_extents (label->layout, NULL, &logical_rect);
+
+ if (logical_rect.height <= height)
+ {
+ width = mid_width;
+ real_width = logical_rect.width;
+ height = logical_rect.height;
+ }
+ }
+ }
+ }
}
+ pango_layout_set_width (label->layout, width);
+
+ requisition->width += real_width / PANGO_SCALE;
+ requisition->height += height / PANGO_SCALE;
}
- gtk_label_finalize_lines_wrap (label, requisition, label->max_width);
}
- else if (!label->words)
+ else /* !label->wrap */
{
- label->max_width = gtk_label_split_text (label);
- gtk_label_finalize_lines (label, requisition, label->max_width);
+ pango_layout_set_width (label->layout, -1);
+ pango_layout_get_extents (label->layout, NULL, &logical_rect);
+
+ requisition->width += logical_rect.width / PANGO_SCALE;
+ requisition->height += logical_rect.height / PANGO_SCALE;
}
}
@@ -861,14 +543,15 @@ gtk_label_style_set (GtkWidget *widget,
g_return_if_fail (GTK_IS_LABEL (widget));
label = GTK_LABEL (widget);
-
- /* Clear the list of words so that they are recomputed on
- * size_request
- */
- if (previous_style && label->words)
- gtk_label_free_words (label);
+
+ if (previous_style && label->layout)
+ {
+ pango_layout_unref (label->layout);
+ label->layout = NULL;
+ }
}
+#if 0
static void
gtk_label_paint_word (GtkLabel *label,
gint x,
@@ -897,6 +580,7 @@ gtk_label_paint_word (GtkLabel *label,
widget, "label",
x + uline->x1, x + uline->x2, y + uline->y);
}
+#endif
static gint
gtk_label_expose (GtkWidget *widget,
@@ -904,8 +588,8 @@ gtk_label_expose (GtkWidget *widget,
{
GtkLabel *label;
GtkMisc *misc;
- GtkLabelWord *word;
gint x, y;
+ gfloat xalign;
g_return_val_if_fail (GTK_IS_LABEL (widget), FALSE);
g_return_val_if_fail (event != NULL, FALSE);
@@ -917,6 +601,11 @@ gtk_label_expose (GtkWidget *widget,
{
misc = GTK_MISC (widget);
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+ xalign = misc->xalign;
+ else
+ xalign = 1. - misc->xalign;
+
/*
* GC Clipping
*/
@@ -924,22 +613,14 @@ gtk_label_expose (GtkWidget *widget,
gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state], &event->area);
x = floor (widget->allocation.x + (gint)misc->xpad
- + (((gint)widget->allocation.width -
- (gint)label->max_width - 2 * (gint)misc->xpad)
- * misc->xalign) + 0.5);
+ + ((widget->allocation.width - widget->requisition.width) * xalign)
+ + 0.5);
y = floor (widget->allocation.y + (gint)misc->ypad
- + (((gint)widget->allocation.height
- - (gint)widget->requisition.height)
- * misc->yalign) + 0.5);
+ + ((widget->allocation.height - widget->requisition.height) * misc->yalign)
+ + 0.5);
- for (word = label->words; word; word = word->next)
- {
- gchar save = word->beginning[word->length];
- word->beginning[word->length] = '\0';
- gtk_label_paint_word (label, x, y, word, &event->area);
- word->beginning[word->length] = save;
- }
+ gdk_draw_layout (widget->window, widget->style->fg_gc [widget->state], x, y, label->layout);
gdk_gc_set_clip_mask (widget->style->white_gc, NULL);
gdk_gc_set_clip_mask (widget->style->fg_gc[widget->state], NULL);
@@ -950,69 +631,80 @@ gtk_label_expose (GtkWidget *widget,
guint
gtk_label_parse_uline (GtkLabel *label,
- const gchar *string)
+ const gchar *str)
{
guint accel_key = GDK_VoidSymbol;
- GdkWChar *p, *q, *string_wc;
- gchar *r;
+
+ gchar *new_str;
gchar *pattern;
- gint length, wc_length;
+ const gchar *src;
+ gchar *dest, *pattern_dest;
gboolean underscore;
-
+
g_return_val_if_fail (GTK_IS_LABEL (label), GDK_VoidSymbol);
- g_return_val_if_fail (string != NULL, GDK_VoidSymbol);
+ g_return_val_if_fail (str != NULL, GDK_VoidSymbol);
/* Convert text to wide characters */
- length = strlen (string);
- string_wc = g_new (GdkWChar, length + 1);
- wc_length = gdk_mbstowcs (string_wc, string, length + 1);
- if (wc_length < 0)
- {
- g_free (string_wc);
- return GDK_VoidSymbol;
- }
- string_wc[wc_length] = '\0';
-
- pattern = g_new (gchar, length+1);
+ new_str = g_new (gchar, strlen (str) + 1);
+ pattern = g_new (gchar, unicode_strlen (str, -1) + 1);
underscore = FALSE;
+
+ src = str;
+ dest = new_str;
+ pattern_dest = pattern;
- p = q = string_wc;
- r = pattern;
-
- while (*p)
+ while (*src)
{
+ unicode_char_t c;
+ gchar *next_src;
+
+ next_src = unicode_get_utf8 (src, &c);
+ if (!next_src)
+ {
+ g_warning ("Invalid input string");
+ g_free (new_str);
+ g_free (pattern);
+ return GDK_VoidSymbol;
+ }
+
if (underscore)
{
- if (*p == '_')
- *r++ = ' ';
+ if (c == '_')
+ *pattern_dest++ = ' ';
else
{
- *r++ = '_';
+ *pattern_dest++ = '_';
if (accel_key == GDK_VoidSymbol)
- accel_key = gdk_keyval_to_lower (*p);
+ accel_key = gdk_keyval_to_lower (c);
}
+
+ while (src < next_src)
+ *dest++ = *src++;
- *q++ = *p;
underscore = FALSE;
}
else
{
- if (*p == '_')
- underscore = TRUE;
+ if (c == '_')
+ {
+ underscore = TRUE;
+ src = next_src;
+ }
else
{
- *q++ = *p;
- *r++ = ' ';
+ while (src < next_src)
+ *dest++ = *src++;
+
+ *pattern_dest++ = ' ';
}
}
- p++;
}
- *q = 0;
- *r = 0;
+ *dest = 0;
+ *pattern_dest = 0;
- gtk_label_set_text_internal (label, gdk_wcstombs (string_wc), string_wc);
+ gtk_label_set_text_internal (label, new_str);
gtk_label_set_pattern (label, pattern);
g_free (pattern);
diff --git a/gtk/gtklabel.h b/gtk/gtklabel.h
index 22cd12861..3abc5af67 100644
--- a/gtk/gtklabel.h
+++ b/gtk/gtklabel.h
@@ -54,14 +54,14 @@ struct _GtkLabel
GtkMisc misc;
gchar *label;
- GdkWChar *label_wc;
- gchar *pattern;
+ gchar *pattern;
- GtkLabelWord *words;
-
- guint max_width : 16;
guint jtype : 2;
- gboolean wrap;
+ gboolean wrap : 1;
+
+ /*< private >*/
+ gint rtl : 2; /* Base dir, cached to detect changes */
+ PangoLayout *layout;
};
struct _GtkLabelClass
@@ -69,18 +69,17 @@ struct _GtkLabelClass
GtkMiscClass parent_class;
};
-GtkType gtk_label_get_type (void);
-GtkWidget * gtk_label_new (const char *str);
-void gtk_label_set_text (GtkLabel *label,
- const char *str);
-void gtk_label_set_justify (GtkLabel *label,
- GtkJustification jtype);
-void gtk_label_set_pattern (GtkLabel *label,
- const gchar *pattern);
-void gtk_label_set_line_wrap (GtkLabel *label,
- gboolean wrap);
-gchar * gtk_label_get_text (GtkLabel *label);
-
+GtkType gtk_label_get_type (void);
+GtkWidget *gtk_label_new (const char *str);
+void gtk_label_set_text (GtkLabel *label,
+ const char *str);
+gchar * gtk_label_get_text (GtkLabel *label);
+void gtk_label_set_justify (GtkLabel *label,
+ GtkJustification jtype);
+void gtk_label_set_pattern (GtkLabel *label,
+ const gchar *pattern);
+void gtk_label_set_line_wrap (GtkLabel *label,
+ gboolean wrap);
/* Convenience function to set the name and pattern by parsing
* a string with embedded underscores, and return the appropriate
* key symbol for the accelerator.
diff --git a/gtk/gtklayout.c b/gtk/gtklayout.c
index 8e70d5ef7..499ea186d 100644
--- a/gtk/gtklayout.c
+++ b/gtk/gtklayout.c
@@ -270,25 +270,52 @@ gtk_layout_move (GtkLayout *layout,
}
}
+static void
+gtk_layout_set_adjustment_upper (GtkAdjustment *adj, gfloat upper)
+{
+ if (upper != adj->upper)
+ {
+ gfloat min = MAX (0., upper - adj->page_size);
+ gboolean value_changed = FALSE;
+
+ adj->upper = upper;
+
+ if (adj->value > min)
+ {
+ adj->value = min;
+ value_changed = TRUE;
+ }
+
+ gtk_signal_emit_by_name (GTK_OBJECT (adj), "changed");
+ if (value_changed)
+ gtk_signal_emit_by_name (GTK_OBJECT (adj), "value_changed");
+ }
+}
+
void
gtk_layout_set_size (GtkLayout *layout,
guint width,
guint height)
{
+ GtkWidget *widget;
+
g_return_if_fail (layout != NULL);
g_return_if_fail (GTK_IS_LAYOUT (layout));
+ widget = GTK_WIDGET (layout);
+
layout->width = width;
layout->height = height;
- layout->hadjustment->upper = layout->width;
- gtk_signal_emit_by_name (GTK_OBJECT (layout->hadjustment), "changed");
-
- layout->vadjustment->upper = layout->height;
- gtk_signal_emit_by_name (GTK_OBJECT (layout->vadjustment), "changed");
+ gtk_layout_set_adjustment_upper (layout->hadjustment, layout->width);
+ gtk_layout_set_adjustment_upper (layout->vadjustment, layout->height);
if (GTK_WIDGET_REALIZED (layout))
- gdk_window_resize (layout->bin_window, width, height);
+ {
+ width = MAX (width, widget->allocation.width);
+ height = MAX (height, widget->allocation.height);
+ gdk_window_resize (layout->bin_window, width, height);
+ }
}
void
@@ -433,8 +460,8 @@ gtk_layout_realize (GtkWidget *widget)
attributes.x = 0;
attributes.y = 0;
- attributes.width = layout->width;
- attributes.height = layout->height;
+ attributes.width = MAX (layout->width, widget->allocation.width);
+ attributes.height = MAX (layout->height, widget->allocation.height);
attributes.event_mask = GDK_EXPOSURE_MASK | GDK_SCROLL_MASK |
gtk_widget_get_events (widget);
@@ -561,18 +588,22 @@ gtk_layout_size_allocate (GtkWidget *widget,
gdk_window_move_resize (widget->window,
allocation->x, allocation->y,
allocation->width, allocation->height);
+
+ gdk_window_resize (layout->bin_window,
+ MAX (layout->width, allocation->width),
+ MAX (layout->height, allocation->height));
}
layout->hadjustment->page_size = allocation->width;
layout->hadjustment->page_increment = allocation->width / 2;
layout->hadjustment->lower = 0;
- layout->hadjustment->upper = layout->width;
+ layout->hadjustment->upper = MAX (allocation->width, layout->width);
gtk_signal_emit_by_name (GTK_OBJECT (layout->hadjustment), "changed");
layout->vadjustment->page_size = allocation->height;
layout->vadjustment->page_increment = allocation->height / 2;
layout->vadjustment->lower = 0;
- layout->vadjustment->upper = layout->height;
+ layout->vadjustment->upper = MAX (allocation->height, layout->height);
gtk_signal_emit_by_name (GTK_OBJECT (layout->vadjustment), "changed");
}
diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c
index 5eca47c7b..952c84134 100644
--- a/gtk/gtkmain.c
+++ b/gtk/gtkmain.c
@@ -150,7 +150,8 @@ static const GDebugKey gtk_debug_keys[] = {
{"misc", GTK_DEBUG_MISC},
{"signals", GTK_DEBUG_SIGNALS},
{"dnd", GTK_DEBUG_DND},
- {"plugsocket", GTK_DEBUG_PLUGSOCKET}
+ {"plugsocket", GTK_DEBUG_PLUGSOCKET},
+ {"text", GTK_DEBUG_TEXT}
};
static const guint gtk_ndebug_keys = sizeof (gtk_debug_keys) / sizeof (GDebugKey);
diff --git a/gtk/gtkmarshal.list b/gtk/gtkmarshal.list
index a016dffc6..f47fb0d62 100644
--- a/gtk/gtkmarshal.list
+++ b/gtk/gtkmarshal.list
@@ -7,6 +7,7 @@ BOOL:POINTER,STRING,STRING,POINTER
ENUM:ENUM
INT:POINTER
INT:POINTER,CHAR,CHAR
+INT:OBJECT,BOXED,POINTER
NONE:BOOL
NONE:BOXED
NONE:ENUM
@@ -14,6 +15,7 @@ NONE:ENUM,FLOAT
NONE:ENUM,FLOAT,BOOL
NONE:INT
NONE:INT,INT
+NONE:INT,INT,INT
NONE:INT,INT,POINTER
NONE:NONE
NONE:OBJECT
diff --git a/gtk/gtkmarshalers.list b/gtk/gtkmarshalers.list
index a016dffc6..f47fb0d62 100644
--- a/gtk/gtkmarshalers.list
+++ b/gtk/gtkmarshalers.list
@@ -7,6 +7,7 @@ BOOL:POINTER,STRING,STRING,POINTER
ENUM:ENUM
INT:POINTER
INT:POINTER,CHAR,CHAR
+INT:OBJECT,BOXED,POINTER
NONE:BOOL
NONE:BOXED
NONE:ENUM
@@ -14,6 +15,7 @@ NONE:ENUM,FLOAT
NONE:ENUM,FLOAT,BOOL
NONE:INT
NONE:INT,INT
+NONE:INT,INT,INT
NONE:INT,INT,POINTER
NONE:NONE
NONE:OBJECT
diff --git a/gtk/gtkprivate.h b/gtk/gtkprivate.h
index a18c4a6bc..06e237932 100644
--- a/gtk/gtkprivate.h
+++ b/gtk/gtkprivate.h
@@ -42,28 +42,26 @@ extern "C" {
typedef enum
{
PRIVATE_GTK_USER_STYLE = 1 << 0,
- PRIVATE_GTK_REDRAW_PENDING = 1 << 1,
PRIVATE_GTK_RESIZE_PENDING = 1 << 2,
PRIVATE_GTK_RESIZE_NEEDED = 1 << 3,
PRIVATE_GTK_LEAVE_PENDING = 1 << 4,
PRIVATE_GTK_HAS_SHAPE_MASK = 1 << 5,
PRIVATE_GTK_IN_REPARENT = 1 << 6,
- PRIVATE_GTK_IS_OFFSCREEN = 1 << 7,
- PRIVATE_GTK_FULLDRAW_PENDING = 1 << 8
+ PRIVATE_GTK_DIRECTION_SET = 1 << 7, /* If the reading direction is not DIR_NONE */
+ PRIVATE_GTK_DIRECTION_LTR = 1 << 8, /* If the reading direction is DIR_LTR */
} GtkPrivateFlags;
/* Macros for extracting a widgets private_flags from GtkWidget.
*/
#define GTK_PRIVATE_FLAGS(wid) (GTK_WIDGET (wid)->private_flags)
#define GTK_WIDGET_USER_STYLE(obj) ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_USER_STYLE) != 0)
-#define GTK_WIDGET_REDRAW_PENDING(obj) ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_REDRAW_PENDING) != 0)
#define GTK_CONTAINER_RESIZE_PENDING(obj) ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_RESIZE_PENDING) != 0)
#define GTK_WIDGET_RESIZE_NEEDED(obj) ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_RESIZE_NEEDED) != 0)
#define GTK_WIDGET_LEAVE_PENDING(obj) ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_LEAVE_PENDING) != 0)
#define GTK_WIDGET_HAS_SHAPE_MASK(obj) ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_HAS_SHAPE_MASK) != 0)
#define GTK_WIDGET_IN_REPARENT(obj) ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_IN_REPARENT) != 0)
-#define GTK_WIDGET_IS_OFFSCREEN(obj) ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_IS_OFFSCREEN) != 0)
-#define GTK_WIDGET_FULLDRAW_PENDING(obj) ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_FULLDRAW_PENDING) != 0)
+#define GTK_WIDGET_DIRECTION_SET(obj) ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_DIRECTION_SET) != 0)
+#define GTK_WIDGET_DIRECTION_LTR(obj) ((GTK_PRIVATE_FLAGS (obj) & PRIVATE_GTK_DIRECTION_LTR) != 0)
/* Macros for setting and clearing private widget flags.
* we use a preprocessor string concatenation here for a clear
diff --git a/gtk/gtkprogressbar.c b/gtk/gtkprogressbar.c
index c0e9af202..5fc297137 100644
--- a/gtk/gtkprogressbar.c
+++ b/gtk/gtkprogressbar.c
@@ -349,6 +349,8 @@ gtk_progress_bar_size_request (GtkWidget *widget,
GtkProgress *progress;
GtkProgressBar *pbar;
gchar *buf;
+ PangoRectangle logical_rect;
+ PangoLayout *layout;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_PROGRESS_BAR (widget));
@@ -357,26 +359,30 @@ gtk_progress_bar_size_request (GtkWidget *widget,
progress = GTK_PROGRESS (widget);
pbar = GTK_PROGRESS_BAR (widget);
+ if (progress->show_text && pbar->bar_style != GTK_PROGRESS_DISCRETE)
+ {
+ buf = gtk_progress_get_text_from_value (progress, progress->adjustment->upper);
+
+ layout = gtk_widget_create_pango_layout (widget);
+ pango_layout_set_text (layout, buf, -1);
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+
+ pango_layout_unref (layout);
+ g_free (buf);
+ }
+
if (pbar->orientation == GTK_PROGRESS_LEFT_TO_RIGHT ||
pbar->orientation == GTK_PROGRESS_RIGHT_TO_LEFT)
{
if (progress->show_text && pbar->bar_style != GTK_PROGRESS_DISCRETE)
{
- buf = gtk_progress_get_text_from_value (progress,
- progress->adjustment->upper);
-
requisition->width = MAX (MIN_HORIZONTAL_BAR_WIDTH,
2 * widget->style->klass->xthickness + 3 +
- gdk_text_width (widget->style->font,
- buf, strlen (buf)) +
- 2 * TEXT_SPACING);
+ logical_rect.width / PANGO_SCALE + 2 * TEXT_SPACING);
requisition->height = MAX (MIN_HORIZONTAL_BAR_HEIGHT,
2 * widget->style->klass->ythickness + 3 +
- gdk_text_height (widget->style->font,
- buf, strlen (buf)) +
- 2 * TEXT_SPACING);
- g_free (buf);
+ logical_rect.height / PANGO_SCALE + 2 * TEXT_SPACING);
}
else
{
@@ -388,21 +394,13 @@ gtk_progress_bar_size_request (GtkWidget *widget,
{
if (progress->show_text && pbar->bar_style != GTK_PROGRESS_DISCRETE)
{
- buf = gtk_progress_get_text_from_value (progress,
- progress->adjustment->upper);
-
requisition->width = MAX (MIN_VERTICAL_BAR_WIDTH,
2 * widget->style->klass->xthickness + 3 +
- gdk_text_width (widget->style->font,
- buf, strlen (buf)) +
- 2 * TEXT_SPACING);
+ logical_rect.width / PANGO_SCALE + 2 * TEXT_SPACING);
requisition->height = MAX (MIN_VERTICAL_BAR_HEIGHT,
2 * widget->style->klass->ythickness + 3 +
- gdk_text_height (widget->style->font,
- buf, strlen (buf)) +
- 2 * TEXT_SPACING);
- g_free (buf);
+ logical_rect.height / PANGO_SCALE + 2 * TEXT_SPACING);
}
else
{
@@ -707,17 +705,23 @@ gtk_progress_bar_paint (GtkProgress *progress)
gint y;
gchar *buf;
GdkRectangle rect;
+ PangoLayout *layout;
+ PangoRectangle logical_rect;
buf = gtk_progress_get_current_text (progress);
+ layout = gtk_widget_create_pango_layout (widget);
+ pango_layout_set_text (layout, buf, -1);
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+
x = widget->style->klass->xthickness + 1 +
(widget->allocation.width - 2 * widget->style->klass->xthickness -
- 3 - gdk_text_width (widget->style->font, buf, strlen (buf)))
+ 3 - logical_rect.width / PANGO_SCALE)
* progress->x_align;
- y = widget->style->font->ascent + 1 +
+ y = widget->style->klass->ythickness + 1 +
(widget->allocation.height - 2 * widget->style->klass->ythickness -
- 3 - gdk_text_height (widget->style->font, buf, strlen (buf)))
+ 3 - logical_rect.height / PANGO_SCALE)
* progress->y_align;
rect.x = widget->style->klass->xthickness + 1;
@@ -730,12 +734,13 @@ gtk_progress_bar_paint (GtkProgress *progress)
gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state],
&rect);
- gdk_draw_text (progress->offscreen_pixmap, widget->style->font,
- widget->style->fg_gc[widget->state],
- x, y, buf, strlen (buf));
+ gdk_draw_layout (progress->offscreen_pixmap,
+ widget->style->fg_gc[widget->state],
+ x, y, layout);
gdk_gc_set_clip_rectangle (widget->style->fg_gc[widget->state],
NULL);
+ pango_layout_unref (layout);
g_free (buf);
}
}
diff --git a/gtk/gtkradiobutton.c b/gtk/gtkradiobutton.c
index 04da03fd0..86dc51849 100644
--- a/gtk/gtkradiobutton.c
+++ b/gtk/gtkradiobutton.c
@@ -417,6 +417,9 @@ gtk_radio_button_draw_indicator (GtkCheckButton *check_button,
else
shadow_type = GTK_SHADOW_OUT;
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+ x = widget->allocation.x + widget->allocation.width - (width + x - widget->allocation.x);
+
gtk_paint_option (widget->style, widget->window,
GTK_WIDGET_STATE (widget), shadow_type,
area, widget, "radiobutton",
diff --git a/gtk/gtkrc.c b/gtk/gtkrc.c
index aed1588f0..84b7b1d67 100644
--- a/gtk/gtkrc.c
+++ b/gtk/gtkrc.c
@@ -125,6 +125,8 @@ static guint gtk_rc_parse_font (GScanner *scanner,
GtkRcStyle *rc_style);
static guint gtk_rc_parse_fontset (GScanner *scanner,
GtkRcStyle *rc_style);
+static guint gtk_rc_parse_font_name (GScanner *scanner,
+ GtkRcStyle *rc_style);
static guint gtk_rc_parse_engine (GScanner *scanner,
GtkRcStyle *rc_style);
static guint gtk_rc_parse_pixmap_path (GScanner *scanner);
@@ -199,6 +201,7 @@ static const struct
{ "text", GTK_RC_TOKEN_TEXT },
{ "font", GTK_RC_TOKEN_FONT },
{ "fontset", GTK_RC_TOKEN_FONTSET },
+ { "font_name", GTK_RC_TOKEN_FONT_NAME },
{ "bg_pixmap", GTK_RC_TOKEN_BG_PIXMAP },
{ "pixmap_path", GTK_RC_TOKEN_PIXMAP_PATH },
{ "style", GTK_RC_TOKEN_STYLE },
@@ -803,10 +806,8 @@ gtk_rc_style_unref (GtkRcStyle *rc_style)
if (rc_style->name)
g_free (rc_style->name);
- if (rc_style->fontset_name)
- g_free (rc_style->fontset_name);
- if (rc_style->font_name)
- g_free (rc_style->font_name);
+ if (rc_style->font_desc)
+ pango_font_description_free (rc_style->font_desc);
for (i=0 ; i<5 ; i++)
if (rc_style->bg_pixmap_name[i])
@@ -1048,8 +1049,8 @@ gtk_rc_add_rc_sets (GSList *slist,
new_style = gtk_rc_style_new ();
*new_style = *rc_style;
new_style->name = g_strdup (rc_style->name);
- new_style->font_name = g_strdup (rc_style->font_name);
- new_style->fontset_name = g_strdup (rc_style->fontset_name);
+ if (rc_style->font_desc)
+ new_style->font_desc = pango_font_description_copy (rc_style->font_desc);
for (i = 0; i < 5; i++)
new_style->bg_pixmap_name[i] = g_strdup (rc_style->bg_pixmap_name[i]);
@@ -1249,26 +1250,20 @@ gtk_rc_style_to_style (GtkRcStyle *rc_style)
style = gtk_style_new ();
style->rc_style = rc_style;
-
- if (rc_style->fontset_name)
- {
- old_font = style->font;
- style->font = gdk_fontset_load (rc_style->fontset_name);
- if (style->font)
- gdk_font_unref (old_font);
- else
- style->font = old_font;
- }
- else if (rc_style->font_name)
+
+ if (rc_style->font_desc)
{
+ pango_font_description_free (style->font_desc);
+ style->font_desc = pango_font_description_copy (rc_style->font_desc);
+
old_font = style->font;
- style->font = gdk_font_load (rc_style->font_name);
+ style->font = gdk_font_from_description (style->font_desc);
if (style->font)
gdk_font_unref (old_font);
else
style->font = old_font;
}
-
+
for (i = 0; i < 5; i++)
{
if (rc_style->color_flags[i] & GTK_RC_FG)
@@ -1349,10 +1344,8 @@ gtk_rc_style_init (GSList *rc_styles)
}
}
- if (!proto_style->font_name && rc_style->font_name)
- proto_style->font_name = g_strdup (rc_style->font_name);
- if (!proto_style->fontset_name && rc_style->fontset_name)
- proto_style->fontset_name = g_strdup (rc_style->fontset_name);
+ if (!proto_style->font_desc && rc_style->font_desc)
+ proto_style->font_desc = pango_font_description_copy (rc_style->font_desc);
if (!proto_style->engine && rc_style->engine)
{
@@ -1505,17 +1498,11 @@ gtk_rc_parse_style (GScanner *scanner)
rc_style->base[i] = parent_style->base[i];
}
- if (parent_style->fontset_name)
- {
- if (rc_style->fontset_name)
- g_free (rc_style->fontset_name);
- rc_style->fontset_name = g_strdup (parent_style->fontset_name);
- }
- else if (parent_style->font_name)
+ if (parent_style->font_desc)
{
- if (rc_style->font_name)
- g_free (rc_style->font_name);
- rc_style->font_name = g_strdup (parent_style->font_name);
+ if (rc_style->font_desc)
+ pango_font_description_free (rc_style->font_desc);
+ rc_style->font_desc = pango_font_description_copy (parent_style->font_desc);
}
for (i = 0; i < 5; i++)
@@ -1562,6 +1549,9 @@ gtk_rc_parse_style (GScanner *scanner)
case GTK_RC_TOKEN_FONTSET:
token = gtk_rc_parse_fontset (scanner, rc_style);
break;
+ case GTK_RC_TOKEN_FONT_NAME:
+ token = gtk_rc_parse_font_name (scanner, rc_style);
+ break;
case GTK_RC_TOKEN_ENGINE:
token = gtk_rc_parse_engine (scanner, rc_style);
break;
@@ -1575,10 +1565,9 @@ gtk_rc_parse_style (GScanner *scanner)
{
if (insert)
{
- if (rc_style->fontset_name)
- g_free (rc_style->fontset_name);
- if (rc_style->font_name)
- g_free (rc_style->font_name);
+ if (rc_style->font_desc)
+ pango_font_description_free (rc_style->font_desc);
+
for (i = 0; i < 5; i++)
if (rc_style->bg_pixmap_name[i])
g_free (rc_style->bg_pixmap_name[i]);
@@ -1594,10 +1583,8 @@ gtk_rc_parse_style (GScanner *scanner)
{
if (insert)
{
- if (rc_style->fontset_name)
- g_free (rc_style->fontset_name);
- if (rc_style->font_name)
- g_free (rc_style->font_name);
+ if (rc_style->font_desc)
+ pango_font_description_free (rc_style->font_desc);
for (i = 0; i < 5; i++)
if (rc_style->bg_pixmap_name[i])
@@ -1849,10 +1836,8 @@ gtk_rc_parse_font (GScanner *scanner,
token = g_scanner_get_next_token (scanner);
if (token != G_TOKEN_STRING)
return G_TOKEN_STRING;
-
- if (rc_style->font_name)
- g_free (rc_style->font_name);
- rc_style->font_name = g_strdup (scanner->value.v_string);
+
+ /* Ignore, do nothing */
return G_TOKEN_NONE;
}
@@ -1874,10 +1859,31 @@ gtk_rc_parse_fontset (GScanner *scanner,
token = g_scanner_get_next_token (scanner);
if (token != G_TOKEN_STRING)
return G_TOKEN_STRING;
+
+ /* Do nothing - silently ignore */
- if (rc_style->fontset_name)
- g_free (rc_style->fontset_name);
- rc_style->fontset_name = g_strdup (scanner->value.v_string);
+ return G_TOKEN_NONE;
+}
+
+static guint
+gtk_rc_parse_font_name (GScanner *scanner,
+ GtkRcStyle *rc_style)
+{
+ guint token;
+
+ token = g_scanner_get_next_token (scanner);
+ if (token != GTK_RC_TOKEN_FONT_NAME)
+ return GTK_RC_TOKEN_FONT;
+
+ token = g_scanner_get_next_token (scanner);
+ if (token != G_TOKEN_EQUAL_SIGN)
+ return G_TOKEN_EQUAL_SIGN;
+
+ token = g_scanner_get_next_token (scanner);
+ if (token != G_TOKEN_STRING)
+ return G_TOKEN_STRING;
+
+ rc_style->font_desc = pango_font_description_from_string (scanner->value.v_string);
return G_TOKEN_NONE;
}
diff --git a/gtk/gtkrc.h b/gtk/gtkrc.h
index 70d8ebaae..30d623cad 100644
--- a/gtk/gtkrc.h
+++ b/gtk/gtkrc.h
@@ -46,9 +46,8 @@ typedef enum {
struct _GtkRcStyle
{
gchar *name;
- gchar *font_name;
- gchar *fontset_name;
gchar *bg_pixmap_name[5];
+ PangoFontDescription *font_desc;
GtkRcFlags color_flags[5];
GdkColor fg[5];
@@ -113,6 +112,7 @@ typedef enum {
GTK_RC_TOKEN_TEXT,
GTK_RC_TOKEN_FONT,
GTK_RC_TOKEN_FONTSET,
+ GTK_RC_TOKEN_FONT_NAME,
GTK_RC_TOKEN_BG_PIXMAP,
GTK_RC_TOKEN_PIXMAP_PATH,
GTK_RC_TOKEN_STYLE,
diff --git a/gtk/gtkscale.c b/gtk/gtkscale.c
index 34e312741..e1099d1ca 100644
--- a/gtk/gtkscale.c
+++ b/gtk/gtkscale.c
@@ -28,7 +28,6 @@
#include "gtkcontainer.h"
#include "gtkscale.h"
-
enum {
ARG_0,
ARG_DIGITS,
@@ -260,25 +259,29 @@ gtk_scale_set_value_pos (GtkScale *scale,
}
}
-gint
-gtk_scale_get_value_width (GtkScale *scale)
+void
+gtk_scale_get_value_size (GtkScale *scale,
+ gint *width,
+ gint *height)
{
GtkRange *range;
- gchar buffer[128];
- gfloat value;
- gint temp;
- gint return_val;
- gint digits;
- gint i, j;
- g_return_val_if_fail (scale != NULL, 0);
- g_return_val_if_fail (GTK_IS_SCALE (scale), 0);
+ g_return_if_fail (scale != NULL);
+ g_return_if_fail (GTK_IS_SCALE (scale));
- return_val = 0;
if (scale->draw_value)
{
+ PangoLayout *layout;
+ PangoRectangle logical_rect;
+ gchar buffer[128];
+ gfloat value;
+ gint digits;
+ gint i, j;
+
range = GTK_RANGE (scale);
+ layout = gtk_widget_create_pango_layout (GTK_WIDGET (scale));
+
value = ABS (range->adjustment->lower);
if (value == 0) value = 1;
digits = log10 (value) + 1;
@@ -296,8 +299,14 @@ gtk_scale_get_value_width (GtkScale *scale)
buffer[i++] = '0';
buffer[i] = '\0';
- return_val = gdk_string_measure (GTK_WIDGET (scale)->style->font, buffer);
+ pango_layout_set_text (layout, buffer, i);
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+ if (width)
+ *width = logical_rect.width / PANGO_SCALE;
+ if (height)
+ *height = logical_rect.width / PANGO_SCALE;
+
value = ABS (range->adjustment->upper);
if (value == 0) value = 1;
digits = log10 (value) + 1;
@@ -315,11 +324,37 @@ gtk_scale_get_value_width (GtkScale *scale)
buffer[i++] = '0';
buffer[i] = '\0';
- temp = gdk_string_measure (GTK_WIDGET (scale)->style->font, buffer);
- return_val = MAX (return_val, temp);
+ pango_layout_set_text (layout, buffer, i);
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+
+ if (width)
+ *width = MAX (*width, logical_rect.width / PANGO_SCALE);
+ if (height)
+ *height = MAX (*height, logical_rect.height / PANGO_SCALE);
+
+ pango_layout_unref (layout);
}
+ else
+ {
+ if (width)
+ *width = 0;
+ if (height)
+ *height = 0;
+ }
+
+}
+
+gint
+gtk_scale_get_value_width (GtkScale *scale)
+{
+ gint width;
+
+ g_return_val_if_fail (scale != NULL, 0);
+ g_return_val_if_fail (GTK_IS_SCALE (scale), 0);
+
+ gtk_scale_get_value_size (scale, &width, NULL);
- return return_val;
+ return width;
}
void
diff --git a/gtk/gtkscale.h b/gtk/gtkscale.h
index 63ab9b0c6..2e2a84317 100644
--- a/gtk/gtkscale.h
+++ b/gtk/gtkscale.h
@@ -66,7 +66,6 @@ struct _GtkScaleClass
void (* draw_value) (GtkScale *scale);
};
-
GtkType gtk_scale_get_type (void);
void gtk_scale_set_digits (GtkScale *scale,
gint digits);
@@ -75,10 +74,12 @@ void gtk_scale_set_draw_value (GtkScale *scale,
void gtk_scale_set_value_pos (GtkScale *scale,
GtkPositionType pos);
gint gtk_scale_get_value_width (GtkScale *scale);
+void gtk_scale_get_value_size (GtkScale *scale,
+ gint *width,
+ gint *height);
void gtk_scale_draw_value (GtkScale *scale);
-
#ifdef __cplusplus
}
#endif /* __cplusplus */
diff --git a/gtk/gtkspinbutton.c b/gtk/gtkspinbutton.c
index 47fccb3a5..3af3a22d7 100644
--- a/gtk/gtkspinbutton.c
+++ b/gtk/gtkspinbutton.c
@@ -513,6 +513,9 @@ gtk_spin_button_size_allocate (GtkWidget *widget,
if (child_allocation.width > ARROW_SIZE + 2 * widget->style->klass->xthickness)
child_allocation.width -= ARROW_SIZE + 2 * widget->style->klass->xthickness;
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+ child_allocation.x += ARROW_SIZE + 2 * widget->style->klass->xthickness;
+
GTK_WIDGET_CLASS (parent_class)->size_allocate (widget, &child_allocation);
widget->allocation = *allocation;
@@ -520,9 +523,14 @@ gtk_spin_button_size_allocate (GtkWidget *widget,
if (GTK_WIDGET_REALIZED (widget))
{
child_allocation.width = ARROW_SIZE + 2 * widget->style->klass->xthickness;
- child_allocation.height = widget->requisition.height;
- child_allocation.x = (allocation->x + allocation->width - ARROW_SIZE -
- 2 * widget->style->klass->xthickness);
+ child_allocation.height = widget->requisition.height;
+
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR)
+ child_allocation.x = (allocation->x + allocation->width - ARROW_SIZE -
+ 2 * widget->style->klass->xthickness);
+ else
+ child_allocation.x = allocation->x;
+
child_allocation.y = allocation->y + (allocation->height - widget->requisition.height) / 2;
gdk_window_move_resize (GTK_SPIN_BUTTON (widget)->panel,
diff --git a/gtk/gtkstyle.c b/gtk/gtkstyle.c
index 498ea40d7..84cc60431 100644
--- a/gtk/gtkstyle.c
+++ b/gtk/gtkstyle.c
@@ -403,10 +403,12 @@ gtk_style_new (void)
style = g_new0 (GtkStyle, 1);
+ style->font_desc = pango_font_description_from_string ("Sans 10");
+
if (!default_font)
{
- default_font =
- gdk_font_load ("-adobe-helvetica-medium-r-normal--*-120-*-*-*-*-iso8859-1");
+ default_font = gdk_font_from_description (style->font_desc);
+
if (!default_font)
default_font = gdk_font_load ("fixed");
if (!default_font)
@@ -746,9 +748,11 @@ gtk_style_destroy (GtkStyle *style)
}
gdk_font_unref (style->font);
+ pango_font_description_free (style->font_desc);
+
if (style->rc_style)
gtk_rc_style_unref (style->rc_style);
-
+
g_free (style);
}
@@ -3141,7 +3145,7 @@ gtk_default_draw_handle (GtkStyle *style,
gtk_paint_box (style, window, state_type, shadow_type, area, widget,
detail, x, y, width, height);
-
+
if (!strcmp (detail, "paned"))
{
/* we want to ignore the shadow border in paned widgets */
diff --git a/gtk/gtkstyle.h b/gtk/gtkstyle.h
index e2de64e04..fca8c21ed 100644
--- a/gtk/gtkstyle.h
+++ b/gtk/gtkstyle.h
@@ -30,6 +30,7 @@
#include <gdk/gdk.h>
#include <gtk/gtkenums.h>
+#include <pango/pango.h>
#ifdef __cplusplus
extern "C" {
@@ -72,6 +73,7 @@ struct _GtkStyle
GdkColor black;
GdkColor white;
GdkFont *font;
+ PangoFontDescription *font_desc;
GdkGC *fg_gc[5];
GdkGC *bg_gc[5];
@@ -96,7 +98,7 @@ struct _GtkStyle
GtkThemeEngine *engine;
gpointer engine_data;
-
+
GtkRcStyle *rc_style; /* the Rc style from which this style
* was created
*/
diff --git a/gtk/gtktable.c b/gtk/gtktable.c
index c22427637..51cda7a22 100644
--- a/gtk/gtktable.c
+++ b/gtk/gtktable.c
@@ -1487,6 +1487,7 @@ gtk_table_size_allocate_pass2 (GtkTable *table)
gint x, y;
gint row, col;
GtkAllocation allocation;
+ GtkWidget *widget = GTK_WIDGET (table);
children = table->children;
while (children)
@@ -1551,6 +1552,10 @@ gtk_table_size_allocate_pass2 (GtkTable *table)
allocation.height = child_requisition.height;
allocation.y = y + (max_height - allocation.height) / 2;
}
+
+ if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+ allocation.x = widget->allocation.x + widget->allocation.width
+ - (allocation.x - widget->allocation.x) - allocation.width;
gtk_widget_size_allocate (child->widget, &allocation);
}
diff --git a/gtk/gtktextbtree.c b/gtk/gtktextbtree.c
new file mode 100644
index 000000000..412a41c3c
--- /dev/null
+++ b/gtk/gtktextbtree.c
@@ -0,0 +1,6349 @@
+/*
+ * gtktextbtree.c --
+ *
+ * This file contains code that manages the B-tree representation
+ * of text for the text buffer and implements character and
+ * toggle segment types.
+ *
+ * Copyright (c) 1992-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 2000 Red Hat, Inc.
+ * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
+ *
+ * This software is copyrighted by the Regents of the University of
+ * California, Sun Microsystems, Inc., and other parties. The
+ * following terms apply to all files associated with the software
+ * unless explicitly disclaimed in individual files.
+ *
+ * The authors hereby grant permission to use, copy, modify,
+ * distribute, and license this software and its documentation for any
+ * purpose, provided that existing copyright notices are retained in
+ * all copies and that this notice is included verbatim in any
+ * distributions. No written agreement, license, or royalty fee is
+ * required for any of the authorized uses. Modifications to this
+ * software may be copyrighted by their authors and need not follow
+ * the licensing terms described here, provided that the new terms are
+ * clearly indicated on the first page of each file where they apply.
+ *
+ * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+ * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
+ * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
+ * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
+ * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
+ * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * GOVERNMENT USE: If you are acquiring this software on behalf of the
+ * U.S. government, the Government shall have only "Restricted Rights"
+ * in the software and related documentation as defined in the Federal
+ * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
+ * are acquiring the software on behalf of the Department of Defense,
+ * the software shall be classified as "Commercial Computer Software"
+ * and the Government shall have only "Restricted Rights" as defined
+ * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
+ * foregoing, the authors grant the U.S. Government and others acting
+ * in its behalf permission to use and distribute the software in
+ * accordance with the terms specified in this license.
+ *
+ */
+
+#include "gtktextbtree.h"
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "gtksignal.h"
+#include "gtktexttag.h"
+#include "gtktexttagtable.h"
+#include "gtktextlayout.h"
+#include "gtktextiterprivate.h"
+#include "gtkdebug.h"
+
+/*
+ * Types
+ */
+
+
+/*
+ * The structure below is used to pass information between
+ * gtk_text_btree_get_tags and inc_count:
+ */
+
+typedef struct TagInfo {
+ int numTags; /* Number of tags for which there
+ * is currently information in
+ * tags and counts. */
+ int arraySize; /* Number of entries allocated for
+ * tags and counts. */
+ GtkTextTag **tags; /* Array of tags seen so far.
+ * Malloc-ed. */
+ int *counts; /* Toggle count (so far) for each
+ * entry in tags. Malloc-ed. */
+} TagInfo;
+
+
+/*
+ * This is used to store per-view width/height info at the tree nodes.
+ */
+
+typedef struct _NodeData NodeData;
+
+struct _NodeData {
+ gpointer view_id;
+ NodeData *next;
+
+ /* Height and width of this node */
+ gint height;
+ gint width : 24;
+
+ /* boolean indicating whether the height/width need to be recomputed */
+ gint valid : 8;
+};
+
+
+/*
+ * The data structure below keeps summary information about one tag as part
+ * of the tag information in a node.
+ */
+
+typedef struct Summary {
+ GtkTextTagInfo *info; /* Handle for tag. */
+ int toggle_count; /* Number of transitions into or
+ * out of this tag that occur in
+ * the subtree rooted at this node. */
+ struct Summary *next; /* Next in list of all tags for same
+ * node, or NULL if at end of list. */
+} Summary;
+
+/*
+ * The data structure below defines a node in the B-tree.
+ */
+
+struct _GtkTextBTreeNode {
+ GtkTextBTreeNode *parent; /* Pointer to parent node, or NULL if
+ * this is the root. */
+ GtkTextBTreeNode *next; /* Next in list of siblings with the
+ * same parent node, or NULL for end
+ * of list. */
+ Summary *summary; /* First in malloc-ed list of info
+ * about tags in this subtree (NULL if
+ * no tag info in the subtree). */
+ int level; /* Level of this node in the B-tree.
+ * 0 refers to the bottom of the tree
+ * (children are lines, not nodes). */
+ union { /* First in linked list of children. */
+ struct _GtkTextBTreeNode *node; /* Used if level > 0. */
+ GtkTextLine *line; /* Used if level == 0. */
+ } children;
+ int num_children; /* Number of children of this node. */
+ int num_lines; /* Total number of lines (leaves) in
+ * the subtree rooted here. */
+ int num_chars; /* Number of chars below here */
+
+ NodeData *node_data;
+};
+
+
+/*
+ * Used to store the list of views in our btree
+ */
+
+typedef struct _BTreeView BTreeView;
+
+struct _BTreeView {
+ gpointer view_id;
+ GtkTextLayout *layout;
+ BTreeView *next;
+ BTreeView *prev;
+};
+
+/*
+ * And the tree itself
+ */
+
+struct _GtkTextBTree {
+ GtkTextBTreeNode *root_node; /* Pointer to root of B-tree. */
+ GtkTextTagTable *table;
+ GHashTable *mark_table;
+ guint refcount;
+ GtkTextLineSegment *insert_mark;
+ GtkTextLineSegment *selection_bound_mark;
+ GtkTextBuffer *buffer;
+ BTreeView *views;
+ GSList *tag_infos;
+ guint tag_changed_handler;
+ guint tag_removed_handler;
+ /* Incremented when a segment with a byte size > 0
+ is added to or removed from the tree (i.e. the
+ length of a line may have changed, and lines may
+ have been added or removed). This invalidates
+ all outstanding iterators.
+ */
+ guint chars_changed_stamp;
+ /* Incremented when any segments are added or deleted;
+ this makes outstanding iterators recalculate their
+ pointed-to segment and segment offset.
+ */
+ guint segments_changed_stamp;
+};
+
+
+/*
+ * Upper and lower bounds on how many children a node may have:
+ * rebalance when either of these limits is exceeded. MAX_CHILDREN
+ * should be twice MIN_CHILDREN and MIN_CHILDREN must be >= 2.
+ */
+
+/* Tk used MAX of 12 and MIN of 6. This makes the tree wide and
+ shallow. It appears to be faster to locate a particular line number
+ if the tree is narrow and deep, since it is more finely sorted. I
+ guess this may increase memory use though, and make it slower to
+ walk the tree in order, or locate a particular byte index (which
+ is done by walking the tree in order).
+
+ There's basically a tradeoff here. However I'm thinking we want to
+ add pixels, byte counts, and char counts to the tree nodes,
+ at that point narrow and deep should speed up all operations,
+ not just the line number searches.
+*/
+
+#if 1
+#define MAX_CHILDREN 12
+#define MIN_CHILDREN 6
+#else
+#define MAX_CHILDREN 6
+#define MIN_CHILDREN 3
+#endif
+
+/*
+ * Prototypes
+ */
+
+static BTreeView *gtk_text_btree_get_view (GtkTextBTree *tree,
+ gpointer view_id);
+static void gtk_text_btree_rebalance (GtkTextBTree *tree,
+ GtkTextBTreeNode *node);
+static GtkTextLine * get_last_line (GtkTextBTree *tree);
+static void post_insert_fixup (GtkTextBTree *tree,
+ GtkTextLine *insert_line,
+ gint char_count_delta,
+ gint line_count_delta);
+static void gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
+ GtkTextTagInfo *info,
+ gint adjust);
+static gboolean gtk_text_btree_node_has_tag (GtkTextBTreeNode *node,
+ GtkTextTag *tag);
+
+static void segments_changed (GtkTextBTree *tree);
+static void chars_changed (GtkTextBTree *tree);
+static void summary_list_destroy (Summary *summary);
+static GtkTextLine *gtk_text_line_new (void);
+static void gtk_text_line_destroy (GtkTextBTree *tree,
+ GtkTextLine *line);
+static void gtk_text_line_set_parent (GtkTextLine *line,
+ GtkTextBTreeNode *node);
+static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
+ gpointer view_id);
+
+
+static NodeData *node_data_new (gpointer view_id);
+static void node_data_destroy (NodeData *nd);
+static void node_data_list_destroy (NodeData *nd);
+static NodeData *node_data_find (NodeData *nd,
+ gpointer view_id);
+
+static GtkTextBTreeNode *gtk_text_btree_node_new (void);
+static void gtk_text_btree_node_invalidate_downward (GtkTextBTreeNode *node);
+static void gtk_text_btree_node_invalidate_upward (GtkTextBTreeNode *node,
+ gpointer view_id);
+static NodeData * gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
+ gpointer view_id);
+static NodeData * gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
+ gpointer view_id);
+static void gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
+ gpointer view_id);
+
+static void gtk_text_btree_node_remove_view (BTreeView *view,
+ GtkTextBTreeNode *node,
+ gpointer view_id);
+static void gtk_text_btree_node_destroy (GtkTextBTree *tree,
+ GtkTextBTreeNode *node);
+static NodeData * gtk_text_btree_node_ensure_data (GtkTextBTreeNode *node,
+ gpointer view_id);
+static void gtk_text_btree_node_remove_data (GtkTextBTreeNode *node,
+ gpointer view_id);
+static void gtk_text_btree_node_get_size (GtkTextBTreeNode *node,
+ gpointer view_id,
+ gint *width,
+ gint *height);
+static GtkTextBTreeNode * gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
+ GtkTextBTreeNode *node2);
+static void get_tree_bounds (GtkTextBTree *tree,
+ GtkTextIter *start,
+ GtkTextIter *end);
+static void tag_changed_cb (GtkTextTagTable *table,
+ GtkTextTag *tag,
+ gboolean size_changed,
+ GtkTextBTree *tree);
+static void tag_removed_cb (GtkTextTagTable *table,
+ GtkTextTag *tag,
+ GtkTextBTree *tree);
+static void cleanup_line (GtkTextLine *line);
+static void recompute_node_counts (GtkTextBTree *tree,
+ GtkTextBTreeNode *node);
+static void inc_count (GtkTextTag *tag,
+ int inc,
+ TagInfo *tagInfoPtr);
+
+static void summary_destroy (Summary *summary);
+
+static void gtk_text_btree_link_segment (GtkTextLineSegment *seg,
+ const GtkTextIter *iter);
+static void gtk_text_btree_unlink_segment (GtkTextBTree *tree,
+ GtkTextLineSegment *seg,
+ GtkTextLine *line);
+
+
+static GtkTextTagInfo *gtk_text_btree_get_tag_info (GtkTextBTree *tree,
+ GtkTextTag *tag);
+static GtkTextTagInfo *gtk_text_btree_get_existing_tag_info (GtkTextBTree *tree,
+ GtkTextTag *tag);
+static void gtk_text_btree_remove_tag_info (GtkTextBTree *tree,
+ GtkTextTag *tag);
+
+
+/* Inline thingies */
+
+static inline void
+segments_changed(GtkTextBTree *tree)
+{
+ tree->segments_changed_stamp += 1;
+}
+
+static inline void
+chars_changed(GtkTextBTree *tree)
+{
+ tree->chars_changed_stamp += 1;
+}
+
+/*
+ * BTree operations
+ */
+
+GtkTextBTree*
+gtk_text_btree_new (GtkTextTagTable *table,
+ GtkTextBuffer *buffer)
+{
+ GtkTextBTree *tree;
+ GtkTextBTreeNode *root_node;
+ GtkTextLine *line, *line2;
+
+ g_return_val_if_fail(GTK_IS_TEXT_TAG_TABLE(table), NULL);
+ g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
+
+ /*
+ * The tree will initially have two empty lines. The second line
+ * isn't actually part of the tree's contents, but its presence
+ * makes several operations easier. The tree will have one GtkTextBTreeNode,
+ * which is also the root of the tree.
+ */
+
+ /* Create the root node. */
+
+ root_node = gtk_text_btree_node_new();
+
+ line = gtk_text_line_new();
+ line2 = gtk_text_line_new();
+
+ root_node->parent = NULL;
+ root_node->next = NULL;
+ root_node->summary = NULL;
+ root_node->level = 0;
+ root_node->children.line = line;
+ root_node->num_children = 2;
+ root_node->num_lines = 2;
+ root_node->num_chars = 2;
+
+ line->parent = root_node;
+ line->next = line2;
+
+ line->segments = char_segment_new("\n", 1);
+
+ line2->parent = root_node;
+ line2->next = NULL;
+ line2->segments = char_segment_new("\n", 1);
+
+ /* Create the tree itself */
+
+ tree = g_new0(GtkTextBTree, 1);
+ tree->root_node = root_node;
+ tree->table = table;
+ tree->views = NULL;
+
+ /* Set these to values that are unlikely to be found
+ in random memory garbage. */
+ tree->chars_changed_stamp = 49;
+ tree->segments_changed_stamp = 243;
+
+ gtk_object_ref(GTK_OBJECT(tree->table));
+ gtk_object_sink(GTK_OBJECT(tree->table));
+
+ tree->tag_changed_handler = gtk_signal_connect(GTK_OBJECT(tree->table),
+ "tag_changed",
+ GTK_SIGNAL_FUNC(tag_changed_cb),
+ tree);
+
+ tree->tag_removed_handler = gtk_signal_connect(GTK_OBJECT(tree->table),
+ "tag_removed",
+ GTK_SIGNAL_FUNC(tag_removed_cb),
+ tree);
+
+ tree->mark_table = g_hash_table_new(g_str_hash, g_str_equal);
+
+ /* We don't ref the buffer, since the buffer owns us;
+ we'd have some circularity issues. The buffer always
+ lasts longer than the BTree
+ */
+ tree->buffer = buffer;
+
+ {
+ GtkTextIter start;
+
+ gtk_text_btree_get_iter_at_line_char(tree, &start, 0, 0);
+
+
+ tree->insert_mark = gtk_text_btree_set_mark(tree,
+ "insert",
+ FALSE,
+ &start,
+ FALSE);
+
+ tree->insert_mark->body.mark.visible = TRUE;
+
+ tree->selection_bound_mark = gtk_text_btree_set_mark(tree,
+ "selection_bound",
+ FALSE,
+ &start,
+ FALSE);
+
+ mark_segment_ref(tree->insert_mark);
+ mark_segment_ref(tree->selection_bound_mark);
+ }
+
+ tree->refcount = 1;
+
+ return tree;
+}
+
+void
+gtk_text_btree_ref (GtkTextBTree *tree)
+{
+ g_return_if_fail(tree != NULL);
+ g_return_if_fail(tree->refcount > 0);
+
+ tree->refcount += 1;
+}
+
+static void
+mark_destroy_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+ mark_segment_unref(value);
+}
+
+void
+gtk_text_btree_unref (GtkTextBTree *tree)
+{
+ g_return_if_fail(tree != NULL);
+ g_return_if_fail(tree->refcount > 0);
+
+ tree->refcount -= 1;
+
+ if (tree->refcount == 0)
+ {
+ gtk_text_btree_node_destroy(tree, tree->root_node);
+
+ g_hash_table_foreach(tree->mark_table,
+ mark_destroy_foreach,
+ NULL);
+ g_hash_table_destroy(tree->mark_table);
+
+ mark_segment_unref(tree->insert_mark);
+ mark_segment_unref(tree->selection_bound_mark);
+
+ gtk_signal_disconnect(GTK_OBJECT(tree->table),
+ tree->tag_changed_handler);
+
+ gtk_signal_disconnect(GTK_OBJECT(tree->table),
+ tree->tag_removed_handler);
+
+ gtk_object_unref(GTK_OBJECT(tree->table));
+
+ g_free(tree);
+ }
+}
+
+GtkTextBuffer*
+gtk_text_btree_get_buffer (GtkTextBTree *tree)
+{
+ return tree->buffer;
+}
+
+guint
+gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree)
+{
+ return tree->chars_changed_stamp;
+}
+
+guint
+gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree)
+{
+ return tree->segments_changed_stamp;
+}
+
+void
+gtk_text_btree_segments_changed (GtkTextBTree *tree)
+{
+ g_return_if_fail(tree != NULL);
+ segments_changed(tree);
+}
+
+/*
+ * Indexable segment mutation
+ */
+
+void
+gtk_text_btree_delete (GtkTextIter *start,
+ GtkTextIter *end)
+{
+ GtkTextLineSegment *prev_seg; /* The segment just before the start
+ * of the deletion range. */
+ GtkTextLineSegment *last_seg; /* The segment just after the end
+ * of the deletion range. */
+ GtkTextLineSegment *seg, *next;
+ GtkTextLine *curline;
+ GtkTextBTreeNode *curnode, *node;
+ GtkTextBTree *tree;
+ GtkTextLine *start_line;
+ GtkTextLine *end_line;
+ GtkTextLine *deleted_lines = NULL; /* List of lines we've deleted */
+ gint start_byte_offset;
+
+ g_return_if_fail(start != NULL);
+ g_return_if_fail(end != NULL);
+ g_return_if_fail(gtk_text_iter_get_btree(start) ==
+ gtk_text_iter_get_btree(end));
+
+ gtk_text_iter_reorder(start, end);
+
+ tree = gtk_text_iter_get_btree(start);
+
+ {
+ /*
+ * The code below is ugly, but it's needed to make sure there
+ * is always a dummy empty line at the end of the text. If the
+ * final newline of the file (just before the dummy line) is being
+ * deleted, then back up index to just before the newline. If
+ * there is a newline just before the first character being deleted,
+ * then back up the first index too, so that an even number of lines
+ * gets deleted. Furthermore, remove any tags that are present on
+ * the newline that isn't going to be deleted after all (this simulates
+ * deleting the newline and then adding a "clean" one back again).
+ */
+
+ gint line1;
+ gint line2;
+
+ line1 = gtk_text_iter_get_line_number(start);
+ line2 = gtk_text_iter_get_line_number(end);
+
+ if (line2 == gtk_text_btree_line_count(tree))
+ {
+ GtkTextTag** tags;
+ int array_size;
+ GtkTextIter orig_end;
+
+ orig_end = *end;
+ gtk_text_iter_backward_char(end);
+
+ --line2;
+
+ if (gtk_text_iter_get_line_char(start) == 0 &&
+ line1 != 0)
+ {
+ gtk_text_iter_backward_char(start);
+ --line1;
+ }
+
+ tags = gtk_text_btree_get_tags(end,
+ &array_size);
+
+ if (tags != NULL)
+ {
+ int i;
+
+ i = 0;
+ while (i < array_size)
+ {
+ gtk_text_btree_tag(end, &orig_end, tags[i], FALSE);
+
+ ++i;
+ }
+
+ g_free(tags);
+ }
+ }
+ }
+
+ /* Broadcast the need for redisplay before we break the iterators */
+ gtk_text_btree_invalidate_region(tree, start, end);
+
+ /* Save the byte offset so we can reset the iterators */
+ start_byte_offset = gtk_text_iter_get_line_byte(start);
+
+ start_line = gtk_text_iter_get_line(start);
+ end_line = gtk_text_iter_get_line(end);
+
+ /*
+ * Split the start and end segments, so we have a place
+ * to insert our new text.
+ *
+ * Tricky point: split at end first; otherwise the split
+ * at end may invalidate seg and/or prev_seg. This allows
+ * us to avoid invalidating segments for start.
+ */
+
+ last_seg = gtk_text_line_segment_split(end);
+ if (last_seg != NULL)
+ last_seg = last_seg->next;
+ else
+ last_seg = end_line->segments;
+
+ prev_seg = gtk_text_line_segment_split(start);
+ if (prev_seg != NULL)
+ {
+ seg = prev_seg->next;
+ prev_seg->next = last_seg;
+ }
+ else
+ {
+ seg = start_line->segments;
+ start_line->segments = last_seg;
+ }
+
+ /* notify iterators that their segments need recomputation,
+ just for robustness. */
+ segments_changed(tree);
+
+ /*
+ * Delete all of the segments between prev_seg and last_seg.
+ */
+
+ curline = start_line;
+ curnode = curline->parent;
+ while (seg != last_seg)
+ {
+ gint char_count = 0;
+
+ if (seg == NULL)
+ {
+ GtkTextLine *nextline;
+
+ /*
+ * We just ran off the end of a line. First find the
+ * next line, then go back to the old line and delete it
+ * (unless it's the starting line for the range).
+ */
+
+ nextline = gtk_text_line_next(curline);
+ if (curline != start_line)
+ {
+ if (curnode == start_line->parent)
+ start_line->next = curline->next;
+ else
+ curnode->children.line = curline->next;
+
+ for (node = curnode; node != NULL;
+ node = node->parent)
+ {
+ node->num_lines -= 1;
+ }
+
+ curnode->num_children -= 1;
+ curline->next = deleted_lines;
+ deleted_lines = curline;
+ }
+
+ curline = nextline;
+ seg = curline->segments;
+
+ /*
+ * If the GtkTextBTreeNode is empty then delete it and its parents,
+ * recursively upwards until a non-empty GtkTextBTreeNode is found.
+ */
+
+ while (curnode->num_children == 0)
+ {
+ GtkTextBTreeNode *parent;
+
+ parent = curnode->parent;
+ if (parent->children.node == curnode)
+ {
+ parent->children.node = curnode->next;
+ }
+ else
+ {
+ GtkTextBTreeNode *prevnode = parent->children.node;
+ while (prevnode->next != curnode)
+ {
+ prevnode = prevnode->next;
+ }
+ prevnode->next = curnode->next;
+ }
+ parent->num_children--;
+ g_free(curnode);
+ curnode = parent;
+ }
+ curnode = curline->parent;
+ continue;
+ }
+
+ next = seg->next;
+ char_count = seg->char_count;
+
+ if ((*seg->type->deleteFunc)(seg, curline, 0) != 0)
+ {
+ /*
+ * This segment refuses to die. Move it to prev_seg and
+ * advance prev_seg if the segment has left gravity.
+ */
+
+ if (prev_seg == NULL)
+ {
+ seg->next = start_line->segments;
+ start_line->segments = seg;
+ }
+ else
+ {
+ seg->next = prev_seg->next;
+ prev_seg->next = seg;
+ }
+ if (seg->type->leftGravity)
+ {
+ prev_seg = seg;
+ }
+ }
+ else
+ {
+ /* Segment is gone. Decrement the char count of the node and
+ all its parents. */
+ for (node = curnode; node != NULL;
+ node = node->parent)
+ {
+ node->num_chars -= char_count;
+ }
+ }
+
+ seg = next;
+ }
+
+ /*
+ * If the beginning and end of the deletion range are in different
+ * lines, join the two lines together and discard the ending line.
+ */
+
+ if (start_line != end_line)
+ {
+ BTreeView *view;
+ GtkTextBTreeNode *ancestor_node;
+
+ GtkTextLine *prevline;
+
+ for (seg = last_seg; seg != NULL;
+ seg = seg->next)
+ {
+ if (seg->type->lineChangeFunc != NULL)
+ {
+ (*seg->type->lineChangeFunc)(seg, end_line);
+ }
+ }
+ curnode = end_line->parent;
+ for (node = curnode; node != NULL;
+ node = node->parent)
+ {
+ node->num_lines--;
+ }
+ curnode->num_children--;
+ prevline = curnode->children.line;
+ if (prevline == end_line)
+ {
+ curnode->children.line = end_line->next;
+ }
+ else
+ {
+ while (prevline->next != end_line)
+ {
+ prevline = prevline->next;
+ }
+ prevline->next = end_line->next;
+ }
+ end_line->next = deleted_lines;
+ deleted_lines = end_line;
+
+ /* We now fix up the per-view aggregates. We add all the height and
+ * width for the deleted lines to the start line, so that when revalidation
+ * occurs, the correct change in size is seen.
+ */
+ ancestor_node = gtk_text_btree_node_common_parent (curnode, start_line->parent);
+ view = tree->views;
+ while (view)
+ {
+ GtkTextLine *line;
+ GtkTextLineData *ld;
+
+ gint deleted_width = 0;
+ gint deleted_height = 0;
+
+ line = deleted_lines;
+ while (line)
+ {
+ GtkTextLine *next_line = line->next;
+ ld = gtk_text_line_get_data (line, view->view_id);
+
+ if (ld)
+ {
+ deleted_width = MAX (deleted_width, ld->width);
+ deleted_height += ld->height;
+ }
+
+ if (!view->next)
+ gtk_text_line_destroy(tree, line);
+
+ line = next_line;
+ }
+
+ if (deleted_width > 0 || deleted_height > 0)
+ {
+ ld = gtk_text_line_get_data (start_line, view->view_id);
+
+ /* FIXME: ld is _NOT_ necessarily non-null here, but there is currently
+ * no way to add ld without also validating the node, which would
+ * be improper at this point.
+ */
+ g_assert (ld);
+
+ ld->width = MAX (deleted_width, ld->width);
+ ld->height += deleted_height;
+ ld->valid = FALSE;
+ }
+
+ gtk_text_btree_node_check_valid_downward (ancestor_node, view->view_id);
+ if (ancestor_node->parent)
+ gtk_text_btree_node_check_valid_upward (ancestor_node->parent, view->view_id);
+
+ view = view->next;
+ }
+
+ gtk_text_btree_rebalance(tree, curnode);
+ }
+
+ /*
+ * Cleanup the segments in the new line.
+ */
+
+ cleanup_line(start_line);
+
+ /*
+ * Lastly, rebalance the first GtkTextBTreeNode of the range.
+ */
+
+ gtk_text_btree_rebalance(tree, start_line->parent);
+
+ /* Notify outstanding iterators that they
+ are now hosed */
+ chars_changed(tree);
+ segments_changed(tree);
+
+ if (gtk_debug_flags & GTK_DEBUG_TEXT)
+ gtk_text_btree_check(tree);
+
+ /* Re-initialize our iterators */
+ gtk_text_btree_get_iter_at_line(tree, start, start_line, start_byte_offset);
+ *end = *start;
+}
+
+void
+gtk_text_btree_insert (GtkTextIter *iter,
+ const gchar *text,
+ gint len)
+{
+ GtkTextLineSegment *prev_seg; /* The segment just before the first
+ * new segment (NULL means new segment
+ * is at beginning of line). */
+ GtkTextLineSegment *cur_seg; /* Current segment; new characters
+ * are inserted just after this one.
+ * NULL means insert at beginning of
+ * line. */
+ GtkTextLine *line; /* Current line (new segments are
+ * added to this line). */
+ GtkTextLineSegment *seg;
+ GtkTextLine *newline;
+ int chunkSize; /* # characters in current chunk. */
+ guint sol; /* start of line */
+ guint eol; /* Pointer to character just after last
+ * one in current chunk. */
+ int line_count_delta; /* Counts change to total number of
+ * lines in file. */
+
+ int char_count_delta; /* change to number of chars */
+ GtkTextBTree *tree;
+ gint start_byte_index;
+ GtkTextLine *start_line;
+
+ g_return_if_fail(text != NULL);
+ g_return_if_fail(iter != NULL);
+
+ if (len < 0)
+ len = strlen(text);
+
+ /* extract iterator info */
+ tree = gtk_text_iter_get_btree(iter);
+ line = gtk_text_iter_get_line(iter);
+ start_line = line;
+ start_byte_index = gtk_text_iter_get_line_byte(iter);
+
+ /* Get our insertion segment split */
+ prev_seg = gtk_text_line_segment_split(iter);
+ cur_seg = prev_seg;
+
+ /* Invalidate all iterators */
+ chars_changed(tree);
+ segments_changed(tree);
+
+ /*
+ * Chop the text up into lines and create a new segment for
+ * each line, plus a new line for the leftovers from the
+ * previous line.
+ */
+
+ eol = 0;
+ sol = 0;
+ line_count_delta = 0;
+ char_count_delta = 0;
+ while (eol < len)
+ {
+ for (; eol < len; eol++)
+ {
+ if (text[eol] == '\n')
+ {
+ eol++;
+ break;
+ }
+ }
+ chunkSize = eol - sol;
+
+ seg = char_segment_new(&text[sol], chunkSize);
+
+ char_count_delta += seg->char_count;
+
+ if (cur_seg == NULL)
+ {
+ seg->next = line->segments;
+ line->segments = seg;
+ }
+ else
+ {
+ seg->next = cur_seg->next;
+ cur_seg->next = seg;
+ }
+
+ if (text[eol-1] != '\n')
+ {
+ break;
+ }
+
+ /*
+ * The chunk ended with a newline, so create a new GtkTextLine
+ * and move the remainder of the old line to it.
+ */
+
+ newline = gtk_text_line_new();
+ gtk_text_line_set_parent(newline, line->parent);
+ newline->next = line->next;
+ line->next = newline;
+ newline->segments = seg->next;
+ seg->next = NULL;
+ line = newline;
+ cur_seg = NULL;
+ line_count_delta++;
+
+ sol = eol;
+ }
+
+ /*
+ * Cleanup the starting line for the insertion, plus the ending
+ * line if it's different.
+ */
+
+ cleanup_line(start_line);
+ if (line != start_line)
+ {
+ cleanup_line(line);
+ }
+
+ post_insert_fixup(tree, line, line_count_delta, char_count_delta);
+
+ /* Invalidate our region, and reset the iterator the user
+ passed in to point to the end of the inserted text. */
+ {
+ GtkTextIter start;
+ GtkTextIter end;
+
+
+ gtk_text_btree_get_iter_at_line(tree,
+ &start,
+ start_line,
+ start_byte_index);
+ end = start;
+
+ /* We could almost certainly be more efficient here
+ by saving the information from the insertion loop
+ above. FIXME */
+ gtk_text_iter_forward_chars(&end, char_count_delta);
+
+ gtk_text_btree_invalidate_region(tree,
+ &start, &end);
+
+
+ /* Convenience for the user */
+ *iter = end;
+ }
+}
+
+void
+gtk_text_btree_insert_pixmap (GtkTextIter *iter,
+ GdkPixmap *pixmap,
+ GdkBitmap *mask)
+{
+ GtkTextLineSegment *seg;
+ GtkTextIter start;
+ GtkTextLineSegment *prevPtr;
+ GtkTextLine *line;
+ GtkTextBTree *tree;
+ gint start_byte_offset;
+
+ line = gtk_text_iter_get_line(iter);
+ tree = gtk_text_iter_get_btree(iter);
+ start_byte_offset = gtk_text_iter_get_line_byte(iter);
+
+ seg = gtk_text_pixmap_segment_new (pixmap, mask);
+
+ prevPtr = gtk_text_line_segment_split(iter);
+ if (prevPtr == NULL)
+ {
+ seg->next = line->segments;
+ line->segments = seg;
+ }
+ else
+ {
+ seg->next = prevPtr->next;
+ prevPtr->next = seg;
+ }
+
+ post_insert_fixup(tree, line, 0, seg->char_count);
+
+ chars_changed(tree);
+ segments_changed(tree);
+
+ /* reset *iter for the user, and invalidate tree nodes */
+
+ gtk_text_btree_get_iter_at_line(tree, &start, line, start_byte_offset);
+
+ *iter = start;
+ gtk_text_iter_forward_char(iter); /* skip forward past the pixmap */
+
+ gtk_text_btree_invalidate_region(tree, &start, iter);
+}
+
+
+/*
+ * View stuff
+ */
+
+static GtkTextLine*
+find_line_by_y(GtkTextBTree *tree, BTreeView *view,
+ GtkTextBTreeNode *node, gint y, gint *line_top,
+ GtkTextLine *last_line)
+{
+ gint current_y = 0;
+
+ if (gtk_debug_flags & GTK_DEBUG_TEXT)
+ gtk_text_btree_check(tree);
+
+ if (node->level == 0)
+ {
+ GtkTextLine *line;
+
+ line = node->children.line;
+
+ while (line != NULL && line != last_line)
+ {
+ GtkTextLineData *ld;
+
+ ld = gtk_text_line_get_data (line, view->view_id);
+
+ if (ld)
+ {
+ if (y < (current_y + (ld ? ld->height : 0)))
+ return line;
+
+ current_y += ld->height;
+ *line_top += ld->height;
+ }
+
+ line = line->next;
+ }
+ return NULL;
+ }
+ else
+ {
+ GtkTextBTreeNode *child;
+
+ child = node->children.node;
+
+ while (child != NULL)
+ {
+ gint width;
+ gint height;
+
+ gtk_text_btree_node_get_size(child, view->view_id,
+ &width, &height);
+
+ if (y < (current_y + height))
+ return find_line_by_y(tree, view, child,
+ y - current_y, line_top,
+ last_line);
+
+ current_y += height;
+ *line_top += height;
+
+ child = child->next;
+ }
+
+ return NULL;
+ }
+}
+
+GtkTextLine *
+gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
+ gpointer view_id,
+ gint ypixel,
+ gint *line_top_out)
+{
+ GtkTextLine *line;
+ BTreeView *view;
+ GtkTextLine *last_line;
+ gint line_top = 0;
+
+ view = gtk_text_btree_get_view (tree, view_id);
+ g_return_val_if_fail (view != NULL, NULL);
+
+ last_line = get_last_line (tree);
+
+ line = find_line_by_y (tree, view, tree->root_node, ypixel, &line_top,
+ last_line);
+
+ if (line_top_out)
+ *line_top_out = line_top;
+
+ return line;
+}
+
+static gint
+find_line_top_in_line_list(GtkTextBTree *tree,
+ BTreeView *view,
+ GtkTextLine *line,
+ GtkTextLine *target_line,
+ gint y)
+{
+ while (line != NULL)
+ {
+ GtkTextLineData *ld;
+
+ if (line == target_line)
+ return y;
+
+ ld = gtk_text_line_get_data (line, view->view_id);
+ if (ld)
+ y += ld->height;
+
+ line = line->next;
+ }
+
+ g_assert_not_reached(); /* If we get here, our
+ target line didn't exist
+ under its parent node */
+ return 0;
+}
+
+gint
+gtk_text_btree_find_line_top (GtkTextBTree *tree,
+ GtkTextLine *target_line,
+ gpointer view_id)
+{
+ gint y = 0;
+ BTreeView *view;
+ GSList *nodes;
+ GSList *iter;
+ GtkTextBTreeNode *node;
+
+ view = gtk_text_btree_get_view(tree, view_id);
+
+ g_return_val_if_fail(view != NULL, 0);
+
+ nodes = NULL;
+ node = target_line->parent;
+ while (node != NULL)
+ {
+ nodes = g_slist_prepend(nodes, node);
+ node = node->parent;
+ }
+
+ iter = nodes;
+ while (iter != NULL)
+ {
+ node = iter->data;
+
+ if (node->level == 0)
+ {
+ g_slist_free(nodes);
+ return find_line_top_in_line_list(tree, view,
+ node->children.line,
+ target_line, y);
+ }
+ else
+ {
+ GtkTextBTreeNode *child;
+ GtkTextBTreeNode *target_node;
+
+ g_assert(iter->next != NULL); /* not at level 0 */
+ target_node = iter->next->data;
+
+ child = node->children.node;
+
+ while (child != NULL)
+ {
+ gint width;
+ gint height;
+
+ if (child == target_node)
+ break;
+ else
+ {
+ gtk_text_btree_node_get_size(child, view->view_id,
+ &width, &height);
+ y += height;
+ }
+ child = child->next;
+ }
+ g_assert(child != NULL); /* should have broken out before we
+ ran out of nodes */
+ }
+
+ iter = g_slist_next(iter);
+ }
+
+ g_assert_not_reached(); /* we return when we find the target line */
+ return 0;
+}
+
+void
+gtk_text_btree_add_view (GtkTextBTree *tree,
+ GtkTextLayout *layout)
+{
+ BTreeView *view;
+ GtkTextLine *last_line;
+ GtkTextLineData *line_data;
+
+ g_return_if_fail(tree != NULL);
+
+ view = g_new(BTreeView, 1);
+
+ view->view_id = layout;
+ view->layout = layout;
+
+ view->next = tree->views;
+ view->prev = NULL;
+
+ tree->views = view;
+
+ /* The last line in the buffer has identity values for the per-view
+ * data so that we can avoid special case checks for it in a large
+ * number of loops
+ */
+ last_line = get_last_line (tree);
+
+ line_data = g_new (GtkTextLineData, 1);
+ line_data->view_id = layout;
+ line_data->next = NULL;
+ line_data->width = 0;
+ line_data->height = 0;
+ line_data->valid = TRUE;
+
+ gtk_text_line_add_data (last_line, line_data);
+}
+
+void
+gtk_text_btree_remove_view (GtkTextBTree *tree,
+ gpointer view_id)
+{
+ BTreeView *view;
+ GtkTextLine *last_line;
+ GtkTextLineData *line_data;
+
+ g_return_if_fail(tree != NULL);
+
+ view = tree->views;
+
+ while (view != NULL)
+ {
+ if (view->view_id == view_id)
+ break;
+
+ view = view->next;
+ }
+
+ g_return_if_fail(view != NULL);
+
+ if (view->next)
+ view->next->prev = view->prev;
+
+ if (view->prev)
+ view->prev->next = view->next;
+
+ if (view == tree->views)
+ tree->views = view->next;
+
+ /* Remove the line data for the last line which we added ourselves.
+ * (Do this first, so that we don't try to call the view's line data destructor on it.)
+ */
+ last_line = get_last_line (tree);
+ line_data = gtk_text_line_remove_data (last_line, view_id);
+ g_free (line_data);
+
+ gtk_text_btree_node_remove_view(view, tree->root_node, view_id);
+
+ g_free(view);
+}
+
+void
+gtk_text_btree_invalidate_region (GtkTextBTree *tree,
+ const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ BTreeView *view;
+
+ view = tree->views;
+
+ while (view != NULL)
+ {
+ gtk_text_layout_invalidate(view->layout, start, end);
+
+ view = view->next;
+ }
+}
+
+void
+gtk_text_btree_get_view_size (GtkTextBTree *tree,
+ gpointer view_id,
+ gint *width,
+ gint *height)
+{
+ g_return_if_fail(tree != NULL);
+ g_return_if_fail(view_id != NULL);
+
+ return gtk_text_btree_node_get_size(tree->root_node, view_id,
+ width, height);
+}
+
+/*
+ * Tag
+ */
+
+typedef struct {
+ GtkTextIter *iters;
+ guint count;
+ guint alloced;
+} IterStack;
+
+static IterStack*
+iter_stack_new(void)
+{
+ IterStack *stack;
+ stack = g_new(IterStack, 1);
+ stack->iters = NULL;
+ stack->count = 0;
+ stack->alloced = 0;
+ return stack;
+}
+
+static void
+iter_stack_push(IterStack *stack, const GtkTextIter *iter)
+{
+ stack->count += 1;
+ if (stack->count > stack->alloced)
+ {
+ stack->alloced = stack->count*2;
+ stack->iters = g_realloc(stack->iters,
+ stack->alloced*sizeof(GtkTextIter));
+ }
+ stack->iters[stack->count-1] = *iter;
+}
+
+static gboolean
+iter_stack_pop(IterStack *stack, GtkTextIter *iter)
+{
+ if (stack->count == 0)
+ return FALSE;
+ else
+ {
+ stack->count -= 1;
+ *iter = stack->iters[stack->count];
+ return TRUE;
+ }
+}
+
+static void
+iter_stack_free(IterStack *stack)
+{
+ g_free(stack->iters);
+ g_free(stack);
+}
+
+static void
+iter_stack_invert(IterStack *stack)
+{
+ if (stack->count > 0)
+ {
+ guint i = 0;
+ guint j = stack->count - 1;
+ while (i < j)
+ {
+ GtkTextIter tmp;
+
+ tmp = stack->iters[i];
+ stack->iters[i] = stack->iters[j];
+ stack->iters[j] = tmp;
+
+ ++i;
+ --j;
+ }
+ }
+}
+
+void
+gtk_text_btree_tag (const GtkTextIter *start_orig,
+ const GtkTextIter *end_orig,
+ GtkTextTag *tag,
+ gboolean add)
+{
+ GtkTextLineSegment *seg, *prev;
+ GtkTextLine *cleanupline;
+ gboolean toggled_on;
+ GtkTextLine *start_line;
+ GtkTextLine *end_line;
+ GtkTextIter iter;
+ GtkTextIter start, end;
+ GtkTextBTree *tree;
+ IterStack *stack;
+ GtkTextTagInfo *info;
+
+ g_return_if_fail(start_orig != NULL);
+ g_return_if_fail(end_orig != NULL);
+ g_return_if_fail(GTK_IS_TEXT_TAG(tag));
+ g_return_if_fail(gtk_text_iter_get_btree(start_orig) ==
+ gtk_text_iter_get_btree(end_orig));
+
+#if 0
+ printf("%s tag %s from %d to %d\n",
+ add ? "Adding" : "Removing",
+ tag->name,
+ gtk_text_iter_get_char_index(start_orig),
+ gtk_text_iter_get_char_index(end_orig));
+#endif
+
+ if (gtk_text_iter_equal(start_orig, end_orig))
+ return;
+
+ start = *start_orig;
+ end = *end_orig;
+
+ gtk_text_iter_reorder(&start, &end);
+
+ tree = gtk_text_iter_get_btree(&start);
+
+ info = gtk_text_btree_get_tag_info(tree, tag);
+
+ start_line = gtk_text_iter_get_line(&start);
+ end_line = gtk_text_iter_get_line(&end);
+
+ /* Find all tag toggles in the region; we are going to delete them.
+ We need to find them in advance, because
+ forward_find_tag_toggle() won't work once we start playing around
+ with the tree. */
+ stack = iter_stack_new();
+ iter = start;
+ /* We don't want to delete a toggle that's at the start iterator. */
+ gtk_text_iter_forward_char(&iter);
+ while (gtk_text_iter_forward_find_tag_toggle(&iter, tag))
+ {
+ if (gtk_text_iter_compare(&iter, &end) >= 0)
+ break;
+ else
+ iter_stack_push(stack, &iter);
+ }
+
+ /* We need to traverse the toggles in order. */
+ iter_stack_invert(stack);
+
+ /*
+ * See whether the tag is present at the start of the range. If
+ * the state doesn't already match what we want then add a toggle
+ * there.
+ */
+
+ toggled_on = gtk_text_iter_has_tag(&start, tag);
+ if ( (add && !toggled_on) ||
+ (!add && toggled_on) )
+ {
+ /* This could create a second toggle at the start position;
+ cleanup_line() will remove it if so. */
+ seg = toggle_segment_new(info, add);
+
+ prev = gtk_text_line_segment_split(&start);
+ if (prev == NULL)
+ {
+ seg->next = start_line->segments;
+ start_line->segments = seg;
+ }
+ else
+ {
+ seg->next = prev->next;
+ prev->next = seg;
+ }
+
+ /* cleanup_line adds the new toggle to the node counts. */
+#if 0
+ printf("added toggle at start\n");
+#endif
+ /* we should probably call segments_changed, but in theory
+ any still-cached segments in the iters we are about to
+ use are still valid, since they're in front
+ of this spot. */
+ }
+
+ /*
+ *
+ * Scan the range of characters and delete any internal tag
+ * transitions. Keep track of what the old state was at the end
+ * of the range, and add a toggle there if it's needed.
+ *
+ */
+
+ cleanupline = start_line;
+ while (iter_stack_pop(stack, &iter))
+ {
+ GtkTextLineSegment *indexable_seg;
+ GtkTextLine *line;
+
+ line = gtk_text_iter_get_line(&iter);
+ seg = gtk_text_iter_get_any_segment(&iter);
+ indexable_seg = gtk_text_iter_get_indexable_segment(&iter);
+
+ g_assert(seg != NULL);
+ g_assert(indexable_seg != NULL);
+ g_assert(seg != indexable_seg);
+
+ prev = line->segments;
+
+ /* Find the segment that actually toggles this tag. */
+ while (seg != indexable_seg)
+ {
+ g_assert(seg != NULL);
+ g_assert(indexable_seg != NULL);
+ g_assert(seg != indexable_seg);
+
+ if ( (seg->type == &gtk_text_toggle_on_type ||
+ seg->type == &gtk_text_toggle_off_type) &&
+ (seg->body.toggle.info == info) )
+ break;
+ else
+ seg = seg->next;
+ }
+
+ g_assert(seg != NULL);
+ g_assert(indexable_seg != NULL);
+
+ g_assert(seg != indexable_seg); /* If this happens, then
+ forward_to_tag_toggle was
+ full of shit. */
+ g_assert(seg->body.toggle.info->tag == tag);
+
+ /* If this happens, when previously tagging we didn't merge
+ overlapping tags. */
+ g_assert( (toggled_on && seg->type == &gtk_text_toggle_off_type) ||
+ (!toggled_on && seg->type == &gtk_text_toggle_on_type) );
+
+ toggled_on = !toggled_on;
+
+#if 0
+ printf("deleting %s toggle\n",
+ seg->type == &gtk_text_toggle_on_type ? "on" : "off");
+#endif
+ /* Remove toggle segment from the list. */
+ if (prev == seg)
+ {
+ line->segments = seg->next;
+ }
+ else
+ {
+ while (prev->next != seg)
+ {
+ prev = prev->next;
+ }
+ prev->next = seg->next;
+ }
+
+ /* Inform iterators we've hosed them. This actually reflects a
+ bit of inefficiency; if you have the same tag toggled on and
+ off a lot in a single line, we keep having the rescan from
+ the front of the line. Of course we have to do that to get
+ "prev" anyway, but here we are doing it an additional
+ time. FIXME */
+ segments_changed(tree);
+
+ /* Update node counts */
+ if (seg->body.toggle.inNodeCounts)
+ {
+ change_node_toggle_count(line->parent,
+ info, -1);
+ seg->body.toggle.inNodeCounts = FALSE;
+ }
+
+ g_free(seg);
+
+ /* We only clean up lines when we're done with them, saves some
+ gratuitous line-segment-traversals */
+
+ if (cleanupline != line)
+ {
+ cleanup_line(cleanupline);
+ cleanupline = line;
+ }
+ }
+
+ iter_stack_free(stack);
+
+ /* toggled_on now reflects the toggle state _just before_ the
+ end iterator. The end iterator could already have a toggle
+ on or a toggle off. */
+ if ( (add && !toggled_on) ||
+ (!add && toggled_on) )
+ {
+ /* This could create a second toggle at the start position;
+ cleanup_line() will remove it if so. */
+
+ seg = toggle_segment_new(info, !add);
+
+ prev = gtk_text_line_segment_split(&end);
+ if (prev == NULL)
+ {
+ seg->next = end_line->segments;
+ end_line->segments = seg;
+ }
+ else
+ {
+ seg->next = prev->next;
+ prev->next = seg;
+ }
+ /* cleanup_line adds the new toggle to the node counts. */
+ g_assert(seg->body.toggle.inNodeCounts == FALSE);
+#if 0
+ printf("added toggle at end\n");
+#endif
+ }
+
+ /*
+ * Cleanup cleanupline and the last line of the range, if
+ * these are different.
+ */
+
+ cleanup_line(cleanupline);
+ if (cleanupline != end_line)
+ {
+ cleanup_line(end_line);
+ }
+
+ segments_changed(tree);
+
+ if (gtk_debug_flags & GTK_DEBUG_TEXT)
+ gtk_text_btree_check(tree);
+}
+
+
+/*
+ * "Getters"
+ */
+
+GtkTextLine*
+gtk_text_btree_get_line (GtkTextBTree *tree,
+ gint line_number,
+ gint *real_line_number)
+{
+ GtkTextBTreeNode *node;
+ GtkTextLine *line;
+ int lines_left;
+ int line_count;
+
+ line_count = gtk_text_btree_line_count(tree);
+
+ if (line_number < 0)
+ {
+ line_number = line_count;
+ }
+ else if (line_number > line_count)
+ {
+ line_number = line_count;
+ }
+
+ if (real_line_number)
+ *real_line_number = line_number;
+
+ node = tree->root_node;
+ lines_left = line_number;
+
+ /*
+ * Work down through levels of the tree until a GtkTextBTreeNode is found at
+ * level 0.
+ */
+
+ while (node->level != 0)
+ {
+ for (node = node->children.node;
+ node->num_lines <= lines_left;
+ node = node->next)
+ {
+#if 0
+ if (node == NULL)
+ {
+ g_error("gtk_text_btree_find_line ran out of GtkTextBTreeNodes");
+ }
+#endif
+ lines_left -= node->num_lines;
+ }
+ }
+
+ /*
+ * Work through the lines attached to the level-0 GtkTextBTreeNode.
+ */
+
+ for (line = node->children.line; lines_left > 0;
+ line = line->next)
+ {
+#if 0
+ if (line == NULL)
+ {
+ g_error("gtk_text_btree_find_line ran out of lines");
+ }
+#endif
+ lines_left -= 1;
+ }
+ return line;
+}
+
+GtkTextLine*
+gtk_text_btree_get_line_at_char(GtkTextBTree *tree,
+ gint char_index,
+ gint *line_start_index,
+ gint *real_char_index)
+{
+ GtkTextBTreeNode *node;
+ GtkTextLine *line;
+ GtkTextLineSegment *seg;
+ int chars_left;
+ int chars_in_line;
+ int bytes_in_line;
+
+ node = tree->root_node;
+
+ /* Clamp to valid indexes (-1 is magic for "highest index") */
+ if (char_index < 0 || char_index >= node->num_chars)
+ {
+ char_index = node->num_chars - 1;
+ }
+
+ *real_char_index = char_index;
+
+ /*
+ * Work down through levels of the tree until a GtkTextBTreeNode is found at
+ * level 0.
+ */
+
+ chars_left = char_index;
+ while (node->level != 0)
+ {
+ for (node = node->children.node;
+ chars_left >= node->num_chars;
+ node = node->next)
+ {
+ chars_left -= node->num_chars;
+
+ g_assert(chars_left >= 0);
+ }
+ }
+
+ if (chars_left == 0)
+ {
+ /* Start of a line */
+
+ *line_start_index = char_index;
+ return node->children.line;
+ }
+
+ /*
+ * Work through the lines attached to the level-0 GtkTextBTreeNode.
+ */
+
+ chars_in_line = 0;
+ bytes_in_line = 0;
+ seg = NULL;
+ for (line = node->children.line; line != NULL; line = line->next)
+ {
+ seg = line->segments;
+ while (seg != NULL)
+ {
+ if (chars_in_line + seg->char_count > chars_left)
+ goto found; /* found our line/segment */
+
+ chars_in_line += seg->char_count;
+
+ seg = seg->next;
+ }
+
+ chars_left -= chars_in_line;
+
+ chars_in_line = 0;
+ seg = NULL;
+ }
+
+ found:
+
+ g_assert(line != NULL); /* hosage, ran out of lines */
+ g_assert(seg != NULL);
+
+ *line_start_index = char_index - chars_left;
+ return line;
+}
+
+GtkTextTag**
+gtk_text_btree_get_tags (const GtkTextIter *iter,
+ gint *num_tags)
+{
+ GtkTextBTreeNode *node;
+ GtkTextLine *siblingline;
+ GtkTextLineSegment *seg;
+ int src, dst, index;
+ TagInfo tagInfo;
+ GtkTextLine *line;
+ GtkTextBTree *tree;
+ gint byte_index;
+
+#define NUM_TAG_INFOS 10
+
+ line = gtk_text_iter_get_line(iter);
+ tree = gtk_text_iter_get_btree(iter);
+ byte_index = gtk_text_iter_get_line_byte(iter);
+
+ tagInfo.numTags = 0;
+ tagInfo.arraySize = NUM_TAG_INFOS;
+ tagInfo.tags = g_new(GtkTextTag*, NUM_TAG_INFOS);
+ tagInfo.counts = g_new(int, NUM_TAG_INFOS);
+
+ /*
+ * Record tag toggles within the line of indexPtr but preceding
+ * indexPtr. Note that if this loop segfaults, your
+ * byte_index probably points past the sum of all
+ * seg->byte_count */
+
+ for (index = 0, seg = line->segments;
+ (index + seg->byte_count) <= byte_index;
+ index += seg->byte_count, seg = seg->next)
+ {
+ if ((seg->type == &gtk_text_toggle_on_type)
+ || (seg->type == &gtk_text_toggle_off_type))
+ {
+ inc_count(seg->body.toggle.info->tag, 1, &tagInfo);
+ }
+ }
+
+ /*
+ * Record toggles for tags in lines that are predecessors of
+ * line but under the same level-0 GtkTextBTreeNode.
+ */
+
+ for (siblingline = line->parent->children.line;
+ siblingline != line;
+ siblingline = siblingline->next)
+ {
+ for (seg = siblingline->segments; seg != NULL;
+ seg = seg->next)
+ {
+ if ((seg->type == &gtk_text_toggle_on_type)
+ || (seg->type == &gtk_text_toggle_off_type))
+ {
+ inc_count(seg->body.toggle.info->tag, 1, &tagInfo);
+ }
+ }
+ }
+
+ /*
+ * For each GtkTextBTreeNode in the ancestry of this line, record tag
+ * toggles for all siblings that precede that GtkTextBTreeNode.
+ */
+
+ for (node = line->parent; node->parent != NULL;
+ node = node->parent)
+ {
+ GtkTextBTreeNode *siblingPtr;
+ Summary *summary;
+
+ for (siblingPtr = node->parent->children.node;
+ siblingPtr != node; siblingPtr = siblingPtr->next)
+ {
+ for (summary = siblingPtr->summary; summary != NULL;
+ summary = summary->next)
+ {
+ if (summary->toggle_count & 1)
+ {
+ inc_count(summary->info->tag, summary->toggle_count,
+ &tagInfo);
+ }
+ }
+ }
+ }
+
+ /*
+ * Go through the tag information and squash out all of the tags
+ * that have even toggle counts (these tags exist before the point
+ * of interest, but not at the desired character itself).
+ */
+
+ for (src = 0, dst = 0; src < tagInfo.numTags; src++)
+ {
+ if (tagInfo.counts[src] & 1)
+ {
+ g_assert(GTK_IS_TEXT_TAG(tagInfo.tags[src]));
+ tagInfo.tags[dst] = tagInfo.tags[src];
+ dst++;
+ }
+ }
+
+ *num_tags = dst;
+ g_free(tagInfo.counts);
+ if (dst == 0)
+ {
+ g_free(tagInfo.tags);
+ return NULL;
+ }
+ return tagInfo.tags;
+}
+
+static void
+copy_segment(GString *string,
+ gboolean include_hidden,
+ gboolean include_nonchars,
+ const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ GtkTextLineSegment *end_seg;
+ GtkTextLineSegment *seg;
+
+ if (gtk_text_iter_equal(start, end))
+ return;
+
+ seg = gtk_text_iter_get_indexable_segment(start);
+ end_seg = gtk_text_iter_get_indexable_segment(end);
+
+ if (seg->type == &gtk_text_char_type)
+ {
+ gboolean copy = TRUE;
+ gint copy_bytes = 0;
+ gint copy_start = 0;
+
+ /* Don't copy if we're elided; segments are elided/not
+ as a whole, no need to check each char */
+ if (!include_hidden &&
+ gtk_text_btree_char_is_invisible(start))
+ {
+ copy = FALSE;
+ /* printf(" <elided>\n"); */
+ }
+
+ copy_start = gtk_text_iter_get_segment_byte(start);
+
+ if (seg == end_seg)
+ {
+ /* End is in the same segment; need to copy fewer bytes. */
+ gint end_byte = gtk_text_iter_get_segment_byte(end);
+
+ copy_bytes = end_byte - copy_start;
+ }
+ else
+ copy_bytes = seg->byte_count;
+
+ g_assert(copy_bytes != 0); /* Due to iter equality check at
+ front of this function. */
+
+ if (copy)
+ {
+ g_assert((copy_start + copy_bytes) <= seg->byte_count);
+
+ g_string_append_len(string,
+ seg->body.chars + copy_start,
+ copy_bytes);
+ }
+
+ /* printf(" :%s\n", string->str); */
+ }
+ else if (seg->type == &gtk_text_pixmap_type)
+ {
+ gboolean copy = TRUE;
+
+ if (!include_nonchars)
+ {
+ copy = FALSE;
+ }
+ else if (!include_hidden &&
+ gtk_text_btree_char_is_invisible(start))
+ {
+ copy = FALSE;
+ }
+
+ if (copy)
+ {
+ g_string_append_len(string,
+ gtk_text_unknown_char_utf8,
+ 3);
+
+ }
+ }
+}
+
+gchar*
+gtk_text_btree_get_text (const GtkTextIter *start_orig,
+ const GtkTextIter *end_orig,
+ gboolean include_hidden,
+ gboolean include_nonchars)
+{
+ GtkTextLineSegment *seg;
+ GtkTextLineSegment *end_seg;
+ GString *retval;
+ GtkTextBTree *tree;
+ gchar *str;
+ GtkTextIter iter;
+ GtkTextIter start;
+ GtkTextIter end;
+
+ g_return_val_if_fail(start_orig != NULL, NULL);
+ g_return_val_if_fail(end_orig != NULL, NULL);
+ g_return_val_if_fail(gtk_text_iter_get_btree(start_orig) ==
+ gtk_text_iter_get_btree(end_orig), NULL);
+
+ start = *start_orig;
+ end = *end_orig;
+
+ gtk_text_iter_reorder(&start, &end);
+
+ retval = g_string_new("");
+
+ tree = gtk_text_iter_get_btree(&start);
+
+ end_seg = gtk_text_iter_get_indexable_segment(&end);
+ iter = start;
+ seg = gtk_text_iter_get_indexable_segment(&iter);
+ while (seg != end_seg)
+ {
+ copy_segment(retval, include_hidden, include_nonchars,
+ &iter, &end);
+
+ if (!gtk_text_iter_forward_indexable_segment(&iter))
+ g_assert_not_reached(); /* must be able to go forward to
+ end_seg, if end_seg still exists
+ and was in front. */
+
+ seg = gtk_text_iter_get_indexable_segment(&iter);
+ }
+
+ str = retval->str;
+ g_string_free(retval, FALSE);
+ return str;
+}
+
+gint
+gtk_text_btree_line_count (GtkTextBTree *tree)
+{
+ /* Subtract bogus line at the end; we return a count
+ of usable lines. */
+ return tree->root_node->num_lines - 1;
+}
+
+gint
+gtk_text_btree_char_count (GtkTextBTree *tree)
+{
+ /* Exclude newline in bogus last line */
+ return tree->root_node->num_chars - 1;
+}
+
+#define LOTSA_TAGS 1000
+gboolean
+gtk_text_btree_char_is_invisible (const GtkTextIter *iter)
+{
+ gboolean invisible = FALSE; /* if nobody says otherwise, it's visible */
+
+ int deftagCnts[LOTSA_TAGS];
+ int *tagCnts = deftagCnts;
+ GtkTextTag *deftags[LOTSA_TAGS];
+ GtkTextTag **tags = deftags;
+ int numTags;
+ GtkTextBTreeNode *node;
+ GtkTextLine *siblingline;
+ GtkTextLineSegment *seg;
+ GtkTextTag *tag;
+ int i, index;
+ GtkTextLine *line;
+ GtkTextBTree *tree;
+ gint byte_index;
+
+ line = gtk_text_iter_get_line(iter);
+ tree = gtk_text_iter_get_btree(iter);
+ byte_index = gtk_text_iter_get_line_byte(iter);
+
+ numTags = gtk_text_tag_table_size(tree->table);
+
+ /* almost always avoid malloc, so stay out of system calls */
+ if (LOTSA_TAGS < numTags)
+ {
+ tagCnts = g_new(int, numTags);
+ tags = g_new(GtkTextTag*, numTags);
+ }
+
+ for (i=0; i<numTags; i++)
+ {
+ tagCnts[i] = 0;
+ }
+
+ /*
+ * Record tag toggles within the line of indexPtr but preceding
+ * indexPtr.
+ */
+
+ for (index = 0, seg = line->segments;
+ (index + seg->byte_count) <= byte_index; /* segfault here means invalid index */
+ index += seg->byte_count, seg = seg->next)
+ {
+ if ((seg->type == &gtk_text_toggle_on_type)
+ || (seg->type == &gtk_text_toggle_off_type))
+ {
+ tag = seg->body.toggle.info->tag;
+ if (tag->elide_set && tag->values->elide)
+ {
+ tags[tag->priority] = tag;
+ tagCnts[tag->priority]++;
+ }
+ }
+ }
+
+ /*
+ * Record toggles for tags in lines that are predecessors of
+ * line but under the same level-0 GtkTextBTreeNode.
+ */
+
+ for (siblingline = line->parent->children.line;
+ siblingline != line;
+ siblingline = siblingline->next)
+ {
+ for (seg = siblingline->segments; seg != NULL;
+ seg = seg->next)
+ {
+ if ((seg->type == &gtk_text_toggle_on_type)
+ || (seg->type == &gtk_text_toggle_off_type))
+ {
+ tag = seg->body.toggle.info->tag;
+ if (tag->elide_set && tag->values->elide)
+ {
+ tags[tag->priority] = tag;
+ tagCnts[tag->priority]++;
+ }
+ }
+ }
+ }
+
+ /*
+ * For each GtkTextBTreeNode in the ancestry of this line, record tag toggles
+ * for all siblings that precede that GtkTextBTreeNode.
+ */
+
+ for (node = line->parent; node->parent != NULL;
+ node = node->parent)
+ {
+ GtkTextBTreeNode *siblingPtr;
+ Summary *summary;
+
+ for (siblingPtr = node->parent->children.node;
+ siblingPtr != node; siblingPtr = siblingPtr->next)
+ {
+ for (summary = siblingPtr->summary; summary != NULL;
+ summary = summary->next)
+ {
+ if (summary->toggle_count & 1)
+ {
+ tag = summary->info->tag;
+ if (tag->elide_set && tag->values->elide)
+ {
+ tags[tag->priority] = tag;
+ tagCnts[tag->priority] += summary->toggle_count;
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Now traverse from highest priority to lowest,
+ * take elided value from first odd count (= on)
+ */
+
+ for (i = numTags-1; i >=0; i--)
+ {
+ if (tagCnts[i] & 1)
+ {
+ /* FIXME not sure this should be if 0 */
+#if 0
+#ifndef ALWAYS_SHOW_SELECTION
+ /* who would make the selection elided? */
+ if ((tag == tkxt->seltag)
+ && !(tkxt->flags & GOT_FOCUS))
+ {
+ continue;
+ }
+#endif
+#endif
+ invisible = tags[i]->values->elide;
+ break;
+ }
+ }
+
+ if (LOTSA_TAGS < numTags)
+ {
+ g_free(tagCnts);
+ g_free(tags);
+ }
+
+ return invisible;
+}
+
+
+/*
+ * Manipulate marks
+ */
+
+static void
+redisplay_region (GtkTextBTree *tree,
+ const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ BTreeView *view;
+ GtkTextLine *start_line, *end_line;
+
+ if (gtk_text_iter_compare (start, end) > 0)
+ {
+ const GtkTextIter *tmp = start;
+ start = end;
+ end = tmp;
+ }
+
+ start_line = gtk_text_iter_get_line (start);
+ end_line = gtk_text_iter_get_line (end);
+
+ view = tree->views;
+ while (view != NULL)
+ {
+ gint start_y, end_y;
+ GtkTextLineData *ld;
+
+ start_y = gtk_text_btree_find_line_top (tree, start_line, view->view_id);
+
+ if (end_line == start_line)
+ end_y = start_y;
+ else
+ end_y = gtk_text_btree_find_line_top (tree, end_line, view->view_id);
+
+ ld = gtk_text_line_get_data (end_line, view->view_id);
+ if (ld)
+ end_y += ld->height;
+
+ gtk_text_layout_changed (view->layout, start_y, end_y - start_y, end_y - start_y);
+
+ view = view->next;
+ }
+}
+
+static void
+redisplay_mark(GtkTextLineSegment *mark)
+{
+ GtkTextIter iter;
+ GtkTextIter end;
+
+ gtk_text_btree_get_iter_at_mark(mark->body.mark.tree,
+ &iter,
+ mark);
+
+ end = iter;
+ gtk_text_iter_forward_char(&end);
+
+ gtk_text_btree_invalidate_region(mark->body.mark.tree,
+ &iter, &end);
+}
+
+static void
+redisplay_mark_if_visible(GtkTextLineSegment *mark)
+{
+ if (!mark->body.mark.visible)
+ return;
+ else
+ redisplay_mark(mark);
+}
+
+static void
+ensure_not_off_end(GtkTextBTree *tree,
+ GtkTextLineSegment *mark,
+ GtkTextIter *iter)
+{
+ if (gtk_text_iter_get_line_number(iter) ==
+ gtk_text_btree_line_count(tree))
+ gtk_text_iter_backward_char(iter);
+}
+
+static GtkTextLineSegment*
+real_set_mark(GtkTextBTree *tree,
+ const gchar *name,
+ gboolean left_gravity,
+ const GtkTextIter *where,
+ gboolean should_exist,
+ gboolean redraw_selections)
+{
+ GtkTextLineSegment *mark;
+ GtkTextIter iter;
+
+ g_return_val_if_fail(tree != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+ g_return_val_if_fail(where != NULL, NULL);
+ g_return_val_if_fail(gtk_text_iter_get_btree(where) == tree, NULL);
+
+ mark = g_hash_table_lookup(tree->mark_table,
+ name);
+
+ if (should_exist && mark == NULL)
+ {
+ g_warning("No mark `%s' exists!", name);
+ return NULL;
+ }
+
+ /* OK if !should_exist and it does already exist, in that case
+ we just move it. */
+
+ iter = *where;
+
+ if (mark != NULL)
+ {
+ if (redraw_selections &&
+ (mark == tree->insert_mark ||
+ mark == tree->selection_bound_mark))
+ {
+ GtkTextIter old_pos;
+
+ gtk_text_btree_get_iter_at_mark (tree, &old_pos, mark);
+ redisplay_region (tree, &old_pos, where);
+ }
+
+ /*
+ * don't let visible marks be after the final newline of the
+ * file.
+ */
+
+ if (mark->body.mark.visible)
+ {
+ ensure_not_off_end(tree, mark, &iter);
+ }
+
+ /* Redraw the mark's old location. */
+ redisplay_mark_if_visible(mark);
+
+ /* Unlink mark from its current location.
+ This could hose our iterator... */
+ gtk_text_btree_unlink_segment(tree, mark,
+ mark->body.mark.line);
+ mark->body.mark.line = gtk_text_iter_get_line(&iter);
+ g_assert(mark->body.mark.line == gtk_text_iter_get_line(&iter));
+
+ segments_changed(tree); /* make sure the iterator recomputes its
+ segment */
+ }
+ else
+ {
+ mark = mark_segment_new(tree,
+ left_gravity,
+ name);
+
+ mark->body.mark.line = gtk_text_iter_get_line(&iter);
+
+ g_hash_table_insert(tree->mark_table,
+ mark->body.mark.name,
+ mark);
+ }
+
+ /* Link mark into new location */
+ gtk_text_btree_link_segment(mark, &iter);
+
+ /* Invalidate some iterators. */
+ segments_changed(tree);
+
+ /*
+ * update the screen at the mark's new location.
+ */
+
+ redisplay_mark_if_visible(mark);
+
+ return mark;
+}
+
+
+GtkTextLineSegment*
+gtk_text_btree_set_mark (GtkTextBTree *tree,
+ const gchar *name,
+ gboolean left_gravity,
+ const GtkTextIter *iter,
+ gboolean should_exist)
+{
+ return real_set_mark(tree, name, left_gravity, iter, should_exist,
+ TRUE);
+}
+
+gboolean
+gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
+ GtkTextIter *start,
+ GtkTextIter *end)
+{
+ gtk_text_btree_get_iter_at_mark (tree, start, tree->insert_mark);
+ gtk_text_btree_get_iter_at_mark (tree, end, tree->selection_bound_mark);
+
+ if (gtk_text_iter_equal(start, end))
+ return FALSE;
+ else
+ {
+ gtk_text_iter_reorder(start, end);
+ return TRUE;
+ }
+}
+
+void
+gtk_text_btree_place_cursor(GtkTextBTree *tree,
+ const GtkTextIter *iter)
+{
+ GtkTextIter start, end;
+
+ if (gtk_text_btree_get_selection_bounds (tree, &start, &end))
+ redisplay_region(tree, &start, &end);
+
+ /* Move insert AND selection_bound before we redisplay */
+ real_set_mark(tree, "insert", FALSE, iter, TRUE, FALSE);
+ real_set_mark(tree, "selection_bound", FALSE, iter, TRUE, FALSE);
+}
+
+void
+gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
+ const gchar *name)
+{
+ GtkTextLineSegment *mark;
+
+ g_return_if_fail(tree != NULL);
+ g_return_if_fail(name != NULL);
+
+ mark = g_hash_table_lookup(tree->mark_table,
+ name);
+
+ gtk_text_btree_remove_mark(tree, mark);
+}
+
+void
+gtk_text_btree_remove_mark (GtkTextBTree *tree,
+ GtkTextLineSegment *segment)
+{
+ g_return_if_fail(segment != NULL);
+ g_return_if_fail(segment != tree->selection_bound_mark);
+ g_return_if_fail(segment != tree->insert_mark);
+ g_return_if_fail(tree != NULL);
+
+ gtk_text_btree_unlink_segment(tree, segment, segment->body.mark.line);
+ /* FIXME should probably cleanup_line but Tk didn't */
+ g_hash_table_remove(tree->mark_table, segment->body.mark.name);
+ mark_segment_unref(segment);
+ segments_changed(tree);
+}
+
+gboolean
+gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
+ GtkTextLineSegment *segment)
+{
+ return segment == tree->insert_mark;
+}
+
+gboolean
+gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
+ GtkTextLineSegment *segment)
+{
+ return segment == tree->selection_bound_mark;
+}
+
+GtkTextLineSegment*
+gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
+ const gchar *name)
+{
+ g_return_val_if_fail(tree != NULL, NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ return g_hash_table_lookup(tree->mark_table, name);
+}
+
+void
+gtk_text_mark_set_visible (GtkTextMark *mark,
+ gboolean setting)
+{
+ GtkTextLineSegment *seg;
+
+ g_return_if_fail(mark != NULL);
+
+ seg = (GtkTextLineSegment*)mark;
+
+ if (seg->body.mark.visible == setting)
+ return;
+ else
+ {
+ seg->body.mark.visible = setting;
+
+ redisplay_mark(seg);
+ }
+}
+
+GtkTextLine*
+gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
+ GtkTextTag *tag)
+{
+ GtkTextBTreeNode *node;
+ GtkTextTagInfo *info;
+
+ g_return_val_if_fail(tree != NULL, NULL);
+
+ if (tag != NULL)
+ {
+ info = gtk_text_btree_get_existing_tag_info(tree, tag);
+
+ if (info == NULL)
+ return NULL;
+
+ if (info->tag_root == NULL)
+ return NULL;
+
+ node = info->tag_root;
+
+ /* We know the tag root has instances of the given
+ tag below it */
+
+ g_assert(node != NULL);
+ while (node->level > 0)
+ {
+ g_assert(node != NULL); /* Failure probably means bad tag summaries. */
+ node = node->children.node;
+ while (node != NULL)
+ {
+ if (gtk_text_btree_node_has_tag(node, tag))
+ goto done;
+ node = node->next;
+ }
+ g_assert(node != NULL);
+ }
+
+ done:
+
+ g_assert(node != NULL); /* The tag summaries said some node had
+ tag toggles... */
+
+ g_assert(node->level == 0);
+
+ return node->children.line;
+ }
+ else
+ {
+ /* Looking for any tag at all (tag == NULL).
+ Unfortunately this can't be done in a simple and efficient way
+ right now; so I'm just going to return the
+ first line in the btree. FIXME */
+ return gtk_text_btree_get_line (tree, 0, NULL);
+ }
+}
+
+GtkTextLine*
+gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
+ GtkTextTag *tag)
+{
+ GtkTextBTreeNode *node;
+ GtkTextBTreeNode *last_node;
+ GtkTextLine *line;
+ GtkTextTagInfo *info;
+
+ g_return_val_if_fail(tree != NULL, NULL);
+
+ if (tag != NULL)
+ {
+ info = gtk_text_btree_get_existing_tag_info(tree, tag);
+
+ if (info->tag_root == NULL)
+ return NULL;
+
+ node = info->tag_root;
+ /* We know the tag root has instances of the given
+ tag below it */
+
+ while (node->level > 0)
+ {
+ g_assert(node != NULL); /* Failure probably means bad tag summaries. */
+ last_node = NULL;
+ node = node->children.node;
+ while (node != NULL)
+ {
+ if (gtk_text_btree_node_has_tag(node, tag))
+ last_node = node;
+ node = node->next;
+ }
+
+ node = last_node;
+ }
+
+ g_assert(node != NULL); /* The tag summaries said some node had
+ tag toggles... */
+
+ g_assert(node->level == 0);
+
+ /* Find the last line in this node */
+ line = node->children.line;
+ while (line->next != NULL)
+ line = line->next;
+
+ return line;
+ }
+ else
+ {
+ /* This search can't be done efficiently at the moment,
+ at least not without complexity.
+ So, we just return the last line.
+ */
+ return gtk_text_btree_get_line (tree, -1, NULL);
+ }
+}
+
+
+/*
+ * Lines
+ */
+
+gint
+gtk_text_line_get_number (GtkTextLine *line)
+{
+ GtkTextLine *line2;
+ GtkTextBTreeNode *node, *parent, *node2;
+ int index;
+
+ /*
+ * First count how many lines precede this one in its level-0
+ * GtkTextBTreeNode.
+ */
+
+ node = line->parent;
+ index = 0;
+ for (line2 = node->children.line; line2 != line;
+ line2 = line2->next)
+ {
+ if (line2 == NULL)
+ {
+ g_error("gtk_text_btree_line_number couldn't find line");
+ }
+ index += 1;
+ }
+
+ /*
+ * Now work up through the levels of the tree one at a time,
+ * counting how many lines are in GtkTextBTreeNodes preceding the current
+ * GtkTextBTreeNode.
+ */
+
+ for (parent = node->parent ; parent != NULL;
+ node = parent, parent = parent->parent)
+ {
+ for (node2 = parent->children.node; node2 != node;
+ node2 = node2->next)
+ {
+ if (node2 == NULL)
+ {
+ g_error("gtk_text_btree_line_number couldn't find GtkTextBTreeNode");
+ }
+ index += node2->num_lines;
+ }
+ }
+ return index;
+}
+
+static GtkTextLineSegment*
+find_toggle_segment_before_char(GtkTextLine *line,
+ gint char_in_line,
+ GtkTextTag *tag)
+{
+ GtkTextLineSegment *seg;
+ GtkTextLineSegment *toggle_seg;
+ int index;
+
+ toggle_seg = NULL;
+ index = 0;
+ seg = line->segments;
+ while ( (index + seg->char_count) <= char_in_line )
+ {
+ if (((seg->type == &gtk_text_toggle_on_type)
+ || (seg->type == &gtk_text_toggle_off_type))
+ && (seg->body.toggle.info->tag == tag))
+ toggle_seg = seg;
+
+ index += seg->char_count;
+ seg = seg->next;
+ }
+
+ return toggle_seg;
+}
+
+static GtkTextLineSegment*
+find_toggle_segment_before_byte(GtkTextLine *line,
+ gint byte_in_line,
+ GtkTextTag *tag)
+{
+ GtkTextLineSegment *seg;
+ GtkTextLineSegment *toggle_seg;
+ int index;
+
+ toggle_seg = NULL;
+ index = 0;
+ seg = line->segments;
+ while ( (index + seg->byte_count) <= byte_in_line )
+ {
+ if (((seg->type == &gtk_text_toggle_on_type)
+ || (seg->type == &gtk_text_toggle_off_type))
+ && (seg->body.toggle.info->tag == tag))
+ toggle_seg = seg;
+
+ index += seg->byte_count;
+ seg = seg->next;
+ }
+
+ return toggle_seg;
+}
+
+static gboolean
+find_toggle_outside_current_line(GtkTextLine *line,
+ GtkTextBTree *tree,
+ GtkTextTag *tag)
+{
+ GtkTextBTreeNode *node;
+ GtkTextLine *sibling_line;
+ GtkTextLineSegment *seg;
+ GtkTextLineSegment *toggle_seg;
+ int toggles;
+ GtkTextTagInfo *info = NULL;
+
+ /*
+ * No toggle in this line. Look for toggles for the tag in lines
+ * that are predecessors of line but under the same
+ * level-0 GtkTextBTreeNode.
+ */
+ toggle_seg = NULL;
+ sibling_line = line->parent->children.line;
+ while (sibling_line != line)
+ {
+ seg = sibling_line->segments;
+ while (seg != NULL)
+ {
+ if (((seg->type == &gtk_text_toggle_on_type)
+ || (seg->type == &gtk_text_toggle_off_type))
+ && (seg->body.toggle.info->tag == tag))
+ toggle_seg = seg;
+
+ seg = seg->next;
+ }
+
+ sibling_line = sibling_line->next;
+ }
+
+ if (toggle_seg != NULL)
+ return (toggle_seg->type == &gtk_text_toggle_on_type);
+
+ /*
+ * No toggle in this GtkTextBTreeNode. Scan upwards through the ancestors of
+ * this GtkTextBTreeNode, counting the number of toggles of the given tag in
+ * siblings that precede that GtkTextBTreeNode.
+ */
+
+ info = gtk_text_btree_get_existing_tag_info(tree, tag);
+
+ if (info == NULL)
+ return FALSE;
+
+ toggles = 0;
+ node = line->parent;
+ while (node->parent != NULL)
+ {
+ GtkTextBTreeNode *sibling_node;
+
+ sibling_node = node->parent->children.node;
+ while (sibling_node != node)
+ {
+ Summary *summary;
+
+ summary = sibling_node->summary;
+ while (summary != NULL)
+ {
+ if (summary->info == info)
+ toggles += summary->toggle_count;
+
+ summary = summary->next;
+ }
+
+ sibling_node = sibling_node->next;
+ }
+
+ if (node == info->tag_root)
+ break;
+
+ node = node->parent;
+ }
+
+ /*
+ * An odd number of toggles means that the tag is present at the
+ * given point.
+ */
+
+ return (toggles & 1) != 0;
+}
+
+/* FIXME this function is far too slow, for no good reason. */
+gboolean
+gtk_text_line_char_has_tag (GtkTextLine *line,
+ GtkTextBTree *tree,
+ gint char_in_line,
+ GtkTextTag *tag)
+{
+ GtkTextLineSegment *toggle_seg;
+
+ g_return_val_if_fail(line != NULL, FALSE);
+
+ /*
+ * Check for toggles for the tag in the line but before
+ * the char. If there is one, its type indicates whether or
+ * not the character is tagged.
+ */
+
+ toggle_seg = find_toggle_segment_before_char(line, char_in_line, tag);
+
+ if (toggle_seg != NULL)
+ return (toggle_seg->type == &gtk_text_toggle_on_type);
+ else
+ return find_toggle_outside_current_line(line, tree, tag);
+}
+
+gboolean
+gtk_text_line_byte_has_tag (GtkTextLine *line,
+ GtkTextBTree *tree,
+ gint byte_in_line,
+ GtkTextTag *tag)
+{
+ GtkTextLineSegment *toggle_seg;
+
+ g_return_val_if_fail(line != NULL, FALSE);
+
+ /*
+ * Check for toggles for the tag in the line but before
+ * the char. If there is one, its type indicates whether or
+ * not the character is tagged.
+ */
+
+ toggle_seg = find_toggle_segment_before_byte(line, byte_in_line, tag);
+
+ if (toggle_seg != NULL)
+ return (toggle_seg->type == &gtk_text_toggle_on_type);
+ else
+ return find_toggle_outside_current_line(line, tree, tag);
+}
+
+GtkTextLine*
+gtk_text_line_next (GtkTextLine *line)
+{
+ GtkTextBTreeNode *node;
+
+ if (line->next != NULL)
+ return line->next;
+ else
+ {
+ /*
+ * This was the last line associated with the particular parent
+ * GtkTextBTreeNode. Search up the tree for the next GtkTextBTreeNode,
+ * then search down from that GtkTextBTreeNode to find the first
+ * line.
+ */
+
+ node = line->parent;
+ while (node != NULL && node->next == NULL)
+ node = node->parent;
+
+ if (node == NULL)
+ return NULL;
+
+ node = node->next;
+ while (node->level > 0)
+ {
+ node = node->children.node;
+ }
+
+ g_assert(node->children.line != line);
+
+ return node->children.line;
+ }
+}
+
+GtkTextLine*
+gtk_text_line_previous (GtkTextLine *line)
+{
+ GtkTextBTreeNode *node;
+ GtkTextBTreeNode *node2;
+ GtkTextLine *prev;
+
+ /*
+ * Find the line under this GtkTextBTreeNode just before the starting line.
+ */
+ prev = line->parent->children.line; /* First line at leaf */
+ while (prev != line)
+ {
+ if (prev->next == line)
+ return prev;
+
+ prev = prev->next;
+
+ if (prev == NULL)
+ g_error("gtk_text_btree_previous_line ran out of lines");
+ }
+
+ /*
+ * This was the first line associated with the particular parent
+ * GtkTextBTreeNode. Search up the tree for the previous GtkTextBTreeNode,
+ * then search down from that GtkTextBTreeNode to find its last line.
+ */
+ for (node = line->parent; ; node = node->parent)
+ {
+ if (node == NULL || node->parent == NULL)
+ return NULL;
+ else if (node != node->parent->children.node)
+ break;
+ }
+
+ for (node2 = node->parent->children.node; ;
+ node2 = node2->children.node)
+ {
+ while (node2->next != node)
+ node2 = node2->next;
+
+ if (node2->level == 0)
+ break;
+
+ node = NULL;
+ }
+
+ for (prev = node2->children.line ; ; prev = prev->next)
+ {
+ if (prev->next == NULL)
+ return prev;
+ }
+
+ g_assert_not_reached();
+ return NULL;
+}
+
+void
+gtk_text_line_add_data (GtkTextLine *line,
+ GtkTextLineData *data)
+{
+ g_return_if_fail(line != NULL);
+ g_return_if_fail(data != NULL);
+ g_return_if_fail(data->view_id != NULL);
+
+ if (line->views)
+ {
+ data->next = line->views;
+ line->views = data;
+ }
+ else
+ {
+ line->views = data;
+ }
+}
+
+gpointer
+gtk_text_line_remove_data (GtkTextLine *line,
+ gpointer view_id)
+{
+ GtkTextLineData *prev;
+ GtkTextLineData *iter;
+
+ g_return_val_if_fail(line != NULL, NULL);
+ g_return_val_if_fail(view_id != NULL, NULL);
+
+ prev = NULL;
+ iter = line->views;
+ while (iter != NULL)
+ {
+ if (iter->view_id == view_id)
+ break;
+ prev = iter;
+ iter = iter->next;
+ }
+
+ if (iter)
+ {
+ if (prev)
+ prev->next = iter->next;
+ else
+ line->views = iter->next;
+
+ return iter;
+ }
+ else
+ return NULL;
+}
+
+gpointer
+gtk_text_line_get_data (GtkTextLine *line,
+ gpointer view_id)
+{
+ GtkTextLineData *iter;
+
+ g_return_val_if_fail(line != NULL, NULL);
+ g_return_val_if_fail(view_id != NULL, NULL);
+
+ iter = line->views;
+ while (iter != NULL)
+ {
+ if (iter->view_id == view_id)
+ break;
+ iter = iter->next;
+ }
+
+ return iter;
+}
+
+void
+gtk_text_line_invalidate_wrap (GtkTextLine *line,
+ GtkTextLineData *ld)
+{
+ /* For now this is totally unoptimized. FIXME?
+
+ We could probably optimize the case where the width removed
+ is less than the max width for the parent node,
+ and the case where the height is unchanged when we re-wrap.
+ */
+
+ g_return_if_fail(ld != NULL);
+
+ ld->valid = FALSE;
+ gtk_text_btree_node_invalidate_upward(line->parent, ld->view_id);
+}
+
+gint
+gtk_text_line_char_count (GtkTextLine *line)
+{
+ GtkTextLineSegment *seg;
+ gint size;
+
+ size = 0;
+ seg = line->segments;
+ while (seg != NULL)
+ {
+ size += seg->char_count;
+ seg = seg->next;
+ }
+ return size;
+}
+
+gint
+gtk_text_line_byte_count (GtkTextLine *line)
+{
+ GtkTextLineSegment *seg;
+ gint size;
+
+ size = 0;
+ seg = line->segments;
+ while (seg != NULL)
+ {
+ size += seg->byte_count;
+ seg = seg->next;
+ }
+
+ return size;
+}
+
+gint
+gtk_text_line_char_index (GtkTextLine *target_line)
+{
+ GSList *node_stack = NULL;
+ GtkTextBTreeNode *iter;
+ GtkTextLine *line;
+ gint num_chars;
+
+ /* Push all our parent nodes onto a stack */
+ iter = target_line->parent;
+
+ g_assert(iter != NULL);
+
+ while (iter != NULL)
+ {
+ node_stack = g_slist_prepend(node_stack, iter);
+
+ iter = iter->parent;
+ }
+
+ /* Check that we have the root node on top of the stack. */
+ g_assert(node_stack != NULL &&
+ node_stack->data != NULL &&
+ ((GtkTextBTreeNode*)node_stack->data)->parent == NULL);
+
+ /* Add up chars in all nodes before the nodes in our stack.
+ */
+
+ num_chars = 0;
+ iter = node_stack->data;
+ while (iter != NULL)
+ {
+ GtkTextBTreeNode *child_iter;
+ GtkTextBTreeNode *next_node;
+
+ next_node = node_stack->next ?
+ node_stack->next->data : NULL;
+ node_stack = g_slist_remove(node_stack, node_stack->data);
+
+ if (iter->level == 0)
+ {
+ /* stack should be empty when we're on the last node */
+ g_assert(node_stack == NULL);
+ break; /* Our children are now lines */
+ }
+
+ g_assert(next_node != NULL);
+ g_assert(iter != NULL);
+ g_assert(next_node->parent == iter);
+
+ /* Add up chars before us in the tree */
+ child_iter = iter->children.node;
+ while (child_iter != next_node)
+ {
+ g_assert(child_iter != NULL);
+
+ num_chars += child_iter->num_chars;
+
+ child_iter = child_iter->next;
+ }
+
+ iter = next_node;
+ }
+
+ g_assert(iter != NULL);
+ g_assert(iter == target_line->parent);
+
+ /* Since we don't store char counts in lines, only in segments, we
+ have to iterate over the lines adding up segment char counts
+ until we find our line. */
+ line = iter->children.line;
+ while (line != target_line)
+ {
+ g_assert(line != NULL);
+
+ num_chars += gtk_text_line_char_count(line);
+
+ line = line->next;
+ }
+
+ g_assert(line == target_line);
+
+ return num_chars;
+}
+
+GtkTextLineSegment*
+gtk_text_line_byte_to_segment (GtkTextLine *line,
+ gint byte_offset,
+ gint *seg_offset)
+{
+ GtkTextLineSegment *seg;
+ int offset;
+
+ g_return_val_if_fail(line != NULL, NULL);
+
+ offset = byte_offset;
+ seg = line->segments;
+
+ while (offset >= seg->byte_count)
+ {
+ g_assert(seg != NULL); /* means an invalid byte index */
+ offset -= seg->byte_count;
+ seg = seg->next;
+ }
+
+ if (seg_offset)
+ *seg_offset = offset;
+
+ return seg;
+}
+
+GtkTextLineSegment*
+gtk_text_line_char_to_segment (GtkTextLine *line,
+ gint char_offset,
+ gint *seg_offset)
+{
+ GtkTextLineSegment *seg;
+ int offset;
+
+ g_return_val_if_fail(line != NULL, NULL);
+
+ offset = char_offset;
+ seg = line->segments;
+
+ while (offset >= seg->char_count)
+ {
+ g_assert(seg != NULL); /* means an invalid char index */
+ offset -= seg->char_count;
+ seg = seg->next;
+ }
+
+ if (seg_offset)
+ *seg_offset = offset;
+
+ return seg;
+}
+
+GtkTextLineSegment*
+gtk_text_line_byte_to_any_segment (GtkTextLine *line,
+ gint byte_offset,
+ gint *seg_offset)
+{
+ GtkTextLineSegment *seg;
+ int offset;
+
+ g_return_val_if_fail(line != NULL, NULL);
+
+ offset = byte_offset;
+ seg = line->segments;
+
+ while (offset > 0 && offset >= seg->byte_count)
+ {
+ g_assert(seg != NULL); /* means an invalid byte index */
+ offset -= seg->byte_count;
+ seg = seg->next;
+ }
+
+ if (seg_offset)
+ *seg_offset = offset;
+
+ return seg;
+}
+
+GtkTextLineSegment*
+gtk_text_line_char_to_any_segment (GtkTextLine *line,
+ gint char_offset,
+ gint *seg_offset)
+{
+ GtkTextLineSegment *seg;
+ int offset;
+
+ g_return_val_if_fail(line != NULL, NULL);
+
+ offset = char_offset;
+ seg = line->segments;
+
+ while (offset > 0 && offset >= seg->char_count)
+ {
+ g_assert(seg != NULL); /* means an invalid byte index */
+ offset -= seg->char_count;
+ seg = seg->next;
+ }
+
+ if (seg_offset)
+ *seg_offset = offset;
+
+ return seg;
+}
+
+gint
+gtk_text_line_byte_to_char (GtkTextLine *line,
+ gint byte_offset)
+{
+ gint char_offset;
+ GtkTextLineSegment *seg;
+
+ g_return_val_if_fail(line != NULL, 0);
+ g_return_val_if_fail(byte_offset >= 0, 0);
+
+ char_offset = 0;
+ seg = line->segments;
+ while (byte_offset >= seg->byte_count) /* while (we need to go farther than
+ the next segment) */
+ {
+ g_assert(seg != NULL); /* our byte_index was bogus if this happens */
+
+ byte_offset -= seg->byte_count;
+ char_offset += seg->char_count;
+
+ seg = seg->next;
+ }
+
+ g_assert(seg != NULL);
+
+ /* Now byte_offset is the offset into the current segment,
+ and char_offset is the start of the current segment.
+ Optimize the case where no chars use > 1 byte */
+ if (seg->byte_count == seg->char_count)
+ return char_offset + byte_offset;
+ else
+ {
+ if (seg->type == &gtk_text_char_type)
+ return char_offset + gtk_text_view_num_utf_chars(seg->body.chars, byte_offset);
+ else
+ {
+ g_assert(seg->char_count == 1);
+ g_assert(byte_offset == 0);
+
+ return char_offset;
+ }
+ }
+}
+
+gint
+gtk_text_line_char_to_byte (GtkTextLine *line,
+ gint char_offset)
+{
+ g_warning("FIXME not implemented");
+}
+
+
+/* FIXME sync with char_locate (or figure out a clean
+ way to merge the two functions) */
+void
+gtk_text_line_byte_locate (GtkTextLine *line,
+ gint byte_offset,
+ GtkTextLineSegment **segment,
+ GtkTextLineSegment **any_segment,
+ gint *seg_byte_offset,
+ gint *line_byte_offset)
+{
+ GtkTextLineSegment *seg;
+ GtkTextLineSegment *after_prev_indexable;
+ GtkTextLineSegment *after_last_indexable;
+ GtkTextLineSegment *last_indexable;
+ gint offset;
+ gint bytes_in_line;
+
+ g_return_if_fail(line != NULL);
+
+ if (byte_offset < 0)
+ {
+ /* -1 means end of line; we here assume no line is
+ longer than 1 bazillion bytes, of course we assumed
+ that anyway since we'd wrap around... */
+
+ byte_offset = G_MAXINT;
+ }
+
+ *segment = NULL;
+ *any_segment = NULL;
+ bytes_in_line = 0;
+
+ offset = byte_offset;
+
+ last_indexable = NULL;
+ after_last_indexable = line->segments;
+ after_prev_indexable = line->segments;
+ seg = line->segments;
+
+ /* The loop ends when we're inside a segment;
+ last_indexable refers to the last segment
+ we passed entirely. */
+ while (seg && offset >= seg->byte_count)
+ {
+ if (seg->char_count > 0)
+ {
+ offset -= seg->byte_count;
+ bytes_in_line += seg->byte_count;
+ last_indexable = seg;
+ after_prev_indexable = after_last_indexable;
+ after_last_indexable = last_indexable->next;
+ }
+
+ seg = seg->next;
+ }
+
+ if (seg == NULL)
+ {
+ /* We went off the end of the line */
+ *segment = last_indexable;
+ *any_segment = after_prev_indexable;
+ /* subtracting 1 is OK, we know it's a newline at the end. */
+ offset = (*segment)->byte_count - 1;
+ bytes_in_line -= (*segment)->byte_count;
+ }
+ else
+ {
+ *segment = seg;
+ if (after_last_indexable != NULL)
+ *any_segment = after_last_indexable;
+ else
+ *any_segment = *segment;
+ }
+
+ /* Override any_segment if we're in the middle of a segment. */
+ if (offset > 0)
+ *any_segment = *segment;
+
+ *seg_byte_offset = offset;
+
+ g_assert(*segment != NULL);
+ g_assert(*any_segment != NULL);
+ g_assert(*seg_byte_offset < (*segment)->byte_count);
+
+ *line_byte_offset = bytes_in_line + *seg_byte_offset;
+}
+
+/* FIXME sync with byte_locate (or figure out a clean
+ way to merge the two functions) */
+void
+gtk_text_line_char_locate (GtkTextLine *line,
+ gint char_offset,
+ GtkTextLineSegment **segment,
+ GtkTextLineSegment **any_segment,
+ gint *seg_char_offset,
+ gint *line_char_offset)
+{
+ GtkTextLineSegment *seg;
+ GtkTextLineSegment *after_prev_indexable;
+ GtkTextLineSegment *after_last_indexable;
+ GtkTextLineSegment *last_indexable;
+ gint offset;
+ gint chars_in_line;
+
+ g_return_if_fail(line != NULL);
+
+ if (char_offset < 0)
+ {
+ /* -1 means end of line; we here assume no line is
+ longer than 1 bazillion chars, of course we assumed
+ that anyway since we'd wrap around... */
+
+ char_offset = G_MAXINT;
+ }
+
+ *segment = NULL;
+ *any_segment = NULL;
+ chars_in_line = 0;
+
+ offset = char_offset;
+
+ last_indexable = NULL;
+ after_last_indexable = line->segments;
+ after_prev_indexable = line->segments;
+ seg = line->segments;
+
+ /* The loop ends when we're inside a segment;
+ last_indexable refers to the last segment
+ we passed entirely. */
+ while (seg && offset >= seg->char_count)
+ {
+ if (seg->char_count > 0)
+ {
+ offset -= seg->char_count;
+ chars_in_line += seg->char_count;
+ last_indexable = seg;
+ after_prev_indexable = after_last_indexable;
+ after_last_indexable = last_indexable->next;
+ }
+
+ seg = seg->next;
+ }
+
+ if (seg == NULL)
+ {
+ /* We went off the end of the line */
+ *segment = last_indexable;
+ *any_segment = after_prev_indexable;
+ /* subtracting 1 is OK, we know it's a newline at the end. */
+ offset = (*segment)->char_count - 1;
+ chars_in_line -= (*segment)->char_count;
+ }
+ else
+ {
+ *segment = seg;
+ if (after_last_indexable != NULL)
+ *any_segment = after_last_indexable;
+ else
+ *any_segment = *segment;
+ }
+
+ /* Override any_segment if we're in the middle of a segment. */
+ if (offset > 0)
+ *any_segment = *segment;
+
+ *seg_char_offset = offset;
+
+ g_assert(*segment != NULL);
+ g_assert(*any_segment != NULL);
+ g_assert(*seg_char_offset < (*segment)->char_count);
+
+ *line_char_offset = chars_in_line + *seg_char_offset;
+}
+
+void
+gtk_text_line_byte_to_char_offsets(GtkTextLine *line,
+ gint byte_offset,
+ gint *line_char_offset,
+ gint *seg_char_offset)
+{
+ GtkTextLineSegment *seg;
+ int offset;
+
+ g_return_if_fail(line != NULL);
+ g_return_if_fail(byte_offset >= 0);
+
+ *line_char_offset = 0;
+
+ offset = byte_offset;
+ seg = line->segments;
+
+ while (offset >= seg->byte_count)
+ {
+ offset -= seg->byte_count;
+ *line_char_offset += seg->char_count;
+ seg = seg->next;
+ g_assert(seg != NULL); /* means an invalid char offset */
+ }
+
+ g_assert(seg->char_count > 0); /* indexable. */
+
+ /* offset is now the number of bytes into the current segment we
+ want to go. Count chars into the current segment. */
+
+ if (seg->type == &gtk_text_char_type)
+ {
+ *seg_char_offset = gtk_text_view_num_utf_chars(seg->body.chars,
+ offset);
+
+ g_assert(*seg_char_offset < seg->char_count);
+
+ *line_char_offset += *seg_char_offset;
+ }
+ else
+ {
+ g_assert(offset == 0);
+ *seg_char_offset = 0;
+ }
+}
+
+void
+gtk_text_line_char_to_byte_offsets(GtkTextLine *line,
+ gint char_offset,
+ gint *line_byte_offset,
+ gint *seg_byte_offset)
+{
+ GtkTextLineSegment *seg;
+ int offset;
+
+ g_return_if_fail(line != NULL);
+ g_return_if_fail(char_offset >= 0);
+
+ *line_byte_offset = 0;
+
+ offset = char_offset;
+ seg = line->segments;
+
+ while (offset >= seg->char_count)
+ {
+ offset -= seg->char_count;
+ *line_byte_offset += seg->byte_count;
+ seg = seg->next;
+ g_assert(seg != NULL); /* means an invalid char offset */
+ }
+
+ g_assert(seg->char_count > 0); /* indexable. */
+
+ /* offset is now the number of chars into the current segment we
+ want to go. Count bytes into the current segment. */
+
+ if (seg->type == &gtk_text_char_type)
+ {
+ *seg_byte_offset = 0;
+ while (offset > 0)
+ {
+ GtkTextUniChar ch;
+ gint bytes;
+
+ bytes = gtk_text_utf_to_unichar(seg->body.chars + *seg_byte_offset,
+ &ch);
+ *seg_byte_offset += bytes;
+ offset -= 1;
+ }
+
+ g_assert(*seg_byte_offset < seg->byte_count);
+
+ *line_byte_offset += *seg_byte_offset;
+ }
+ else
+ {
+ g_assert(offset == 0);
+ *seg_byte_offset = 0;
+ }
+}
+
+/* remember that tag == NULL means "any tag" */
+GtkTextLine*
+gtk_text_line_next_could_contain_tag(GtkTextLine *line,
+ GtkTextBTree *tree,
+ GtkTextTag *tag)
+{
+ GtkTextBTreeNode *node;
+ GtkTextTagInfo *info;
+ gboolean below_tag_root;
+
+ g_return_val_if_fail(line != NULL, NULL);
+
+ if (gtk_debug_flags & GTK_DEBUG_TEXT)
+ gtk_text_btree_check (tree);
+
+ if (tag == NULL)
+ {
+ /* Right now we can only offer linear-search if the user wants
+ to know about any tag toggle at all. */
+ return gtk_text_line_next (line);
+ }
+
+ /* Our tag summaries only have node precision, not line
+ precision. This means that if any line under a node could contain a
+ tag, then any of the others could also contain a tag.
+
+ In the future we could have some mechanism to keep track of how
+ many toggles we've found under a node so far, since we have a
+ count of toggles under the node. But for now I'm going with KISS.
+ */
+
+ /* return same-node line, if any. */
+ if (line->next)
+ return line->next;
+
+ info = gtk_text_btree_get_existing_tag_info(tree, tag);
+ if (info == NULL)
+ return NULL;
+
+ if (info->tag_root == NULL)
+ return NULL;
+
+ /* We need to go up out of this node, and on to the next one with
+ toggles for the target tag. If we're below the tag root, we need to
+ find the next node below the tag root that has tag summaries. If
+ we're not below the tag root, we need to see if the tag root is
+ after us in the tree, and if so, return the first line underneath
+ the tag root. */
+
+ node = line->parent;
+ below_tag_root = FALSE;
+ while (node != NULL)
+ {
+ if (node == info->tag_root)
+ {
+ below_tag_root = TRUE;
+ break;
+ }
+
+ node = node->parent;
+ }
+
+ if (below_tag_root)
+ {
+ node = line->parent;
+ while (node != info->tag_root)
+ {
+ if (node->next == NULL)
+ node = node->parent;
+ else
+ {
+ node = node->next;
+
+ if (gtk_text_btree_node_has_tag(node, tag))
+ goto found;
+ }
+ }
+ return NULL;
+ }
+ else
+ {
+ GtkTextBTreeNode * iter;
+ GtkTextBTreeNode * common_parent;
+ GtkTextBTreeNode * parent_of_tag_root;
+ GtkTextBTreeNode * parent_of_node;
+
+ /* Find common parent between our current line, and the tag
+ root. Save the child nodes of the common parent we used to get
+ to the common parent; we then use these two child nodes to
+ determine whether the ordering of the tag root and the current
+ line in the tree. (Nice code cleanup: write
+ gtk_btree_node_compare() to compute node ordering.)
+ */
+
+ /* Get on the same level */
+ node = line->parent;
+ while (node->level < info->tag_root->level)
+ node = node->parent;
+
+ common_parent = info->tag_root->parent;
+
+ /* Find common parent, and children of that parent above
+ tag root and our current node */
+ parent_of_node = node;
+ parent_of_tag_root = info->tag_root;
+
+ while (node->parent != common_parent)
+ {
+ parent_of_node = node;
+ parent_of_tag_root = common_parent;
+ node = node->parent;
+ common_parent = common_parent->parent;
+ }
+
+ /* See which is first in the list of common_parent's children */
+ iter = common_parent->children.node;
+ while (iter != NULL)
+ {
+ if (iter == parent_of_tag_root)
+ return NULL; /* Tag root was before us in the tree */
+ else if (iter == parent_of_node)
+ {
+ /* We want the first inside-tag-root node,
+ since we're before the tag root */
+ node = info->tag_root;
+ goto found;
+ }
+
+ iter = iter->next;
+ }
+
+ return NULL;
+ }
+
+ found:
+
+ g_assert(node != NULL);
+
+ /* We have to find the first sub-node of this node that contains
+ the target tag. */
+
+ while (node->level > 0)
+ {
+ g_assert(node != NULL); /* If this fails, it likely means an
+ incorrect tag summary led us on a
+ wild goose chase down this branch of
+ the tree. */
+ node = node->children.node;
+ while (node != NULL)
+ {
+ if (gtk_text_btree_node_has_tag(node, tag))
+ goto done;
+ node = node->next;
+ }
+ g_assert(node != NULL);
+ }
+
+ done:
+ g_assert(node != NULL);
+ g_assert(node->level == 0);
+
+ return node->children.line;
+}
+
+GtkTextLine*
+gtk_text_line_previous_could_contain_tag(GtkTextLine *line,
+ GtkTextBTree *tree,
+ GtkTextTag *tag)
+{
+ g_warning("FIXME");
+
+}
+
+/*
+ * Non-public function implementations */
+
+static void
+summary_list_destroy(Summary *summary)
+{
+ Summary *next;
+ while (summary != NULL)
+ {
+ next = summary->next;
+ summary_destroy (summary);
+ summary = next;
+ }
+}
+
+static GtkTextLine*
+get_last_line(GtkTextBTree *tree)
+{
+ gint n_lines;
+ GtkTextLine *line;
+ gint real_line;
+
+ n_lines = gtk_text_btree_line_count(tree);
+
+ g_assert(n_lines >= 1); /* num_lines doesn't return bogus last line. */
+
+ line = gtk_text_btree_get_line(tree, n_lines, &real_line);
+
+ return line;
+}
+
+/*
+ * Lines
+ */
+
+static GtkTextLine*
+gtk_text_line_new(void)
+{
+ GtkTextLine *line;
+
+ line = g_new0(GtkTextLine, 1);
+
+ return line;
+}
+
+static void
+gtk_text_line_destroy(GtkTextBTree *tree, GtkTextLine *line)
+{
+ GtkTextLineData *ld;
+ GtkTextLineData *next;
+
+ g_return_if_fail(line != NULL);
+
+ ld = line->views;
+ while (ld != NULL)
+ {
+ BTreeView *view;
+
+ view = gtk_text_btree_get_view (tree, ld->view_id);
+
+ g_assert(view != NULL);
+
+ next = ld->next;
+ gtk_text_layout_free_line_data (view->layout, line, ld);
+
+ ld = next;
+ }
+
+ g_free(line);
+}
+
+static void
+gtk_text_line_set_parent(GtkTextLine *line,
+ GtkTextBTreeNode *node)
+{
+ if (line->parent == node)
+ return;
+ line->parent = node;
+ gtk_text_btree_node_invalidate_upward(node, NULL);
+}
+
+static void
+cleanup_line(GtkTextLine *line)
+{
+ GtkTextLineSegment *seg, **prev_p;
+ gboolean changed;
+
+ /*
+ * Make a pass over all of the segments in the line, giving each
+ * a chance to clean itself up. This could potentially change
+ * the structure of the line, e.g. by merging two segments
+ * together or having two segments cancel themselves; if so,
+ * then repeat the whole process again, since the first structure
+ * change might make other structure changes possible. Repeat
+ * until eventually there are no changes.
+ */
+
+ changed = TRUE;
+ while (changed)
+ {
+ changed = FALSE;
+ for (prev_p = &line->segments, seg = *prev_p;
+ seg != NULL;
+ prev_p = &(*prev_p)->next, seg = *prev_p)
+ {
+ if (seg->type->cleanupFunc != NULL)
+ {
+ *prev_p = (*seg->type->cleanupFunc)(seg, line);
+ if (seg != *prev_p)
+ changed = TRUE;
+ }
+ }
+ }
+}
+
+/*
+ * Nodes
+ */
+
+static NodeData*
+node_data_new(gpointer view_id)
+{
+ NodeData *nd;
+
+ nd = g_new(NodeData, 1);
+
+ nd->view_id = view_id;
+ nd->next = NULL;
+ nd->width = 0;
+ nd->height = 0;
+ nd->valid = FALSE;
+
+ return nd;
+}
+
+static void
+node_data_destroy(NodeData *nd)
+{
+
+ g_free(nd);
+}
+
+static void
+node_data_list_destroy(NodeData *nd)
+{
+ NodeData *iter;
+ NodeData *next;
+
+ iter = nd;
+ while (iter != NULL)
+ {
+ next = iter->next;
+ node_data_destroy(iter);
+ iter = next;
+ }
+}
+
+static NodeData*
+node_data_find(NodeData *nd, gpointer view_id)
+{
+ while (nd != NULL)
+ {
+ if (nd->view_id == view_id)
+ break;
+ nd = nd->next;
+ }
+ return nd;
+}
+
+static void
+summary_destroy (Summary *summary)
+{
+ /* Fill with error-triggering garbage */
+ summary->info = (void*)0x1;
+ summary->toggle_count = 567;
+ summary->next = (void*)0x1;
+ g_free (summary);
+}
+
+static GtkTextBTreeNode*
+gtk_text_btree_node_new(void)
+{
+ GtkTextBTreeNode *node;
+
+ node = g_new(GtkTextBTreeNode, 1);
+
+ node->node_data = NULL;
+
+ return node;
+}
+
+static void
+gtk_text_btree_node_adjust_toggle_count (GtkTextBTreeNode *node,
+ GtkTextTagInfo *info,
+ gint adjust)
+{
+ Summary *summary;
+
+ summary = node->summary;
+ while (summary != NULL)
+ {
+ if (summary->info == info)
+ {
+ summary->toggle_count += adjust;
+ break;
+ }
+
+ summary = summary->next;
+ }
+
+ if (summary == NULL)
+ {
+ /* didn't find a summary for our tag. */
+ g_return_if_fail(adjust > 0);
+ summary = g_new(Summary, 1);
+ summary->info = info;
+ summary->toggle_count = adjust;
+ summary->next = node->summary;
+ node->summary = summary;
+ }
+}
+
+/* Note that the tag root and above do not have summaries
+ for the tag; only nodes below the tag root have
+ the summaries. */
+static gboolean
+gtk_text_btree_node_has_tag (GtkTextBTreeNode *node, GtkTextTag *tag)
+{
+ Summary *summary;
+
+ summary = node->summary;
+ while (summary != NULL)
+ {
+ if (tag == NULL ||
+ summary->info->tag == tag)
+ return TRUE;
+
+ summary = summary->next;
+ }
+
+ return FALSE;
+}
+
+/* Add node and all children to the damage region. */
+static void
+gtk_text_btree_node_invalidate_downward(GtkTextBTreeNode *node)
+{
+ NodeData *nd;
+
+ nd = node->node_data;
+ while (nd != NULL)
+ {
+ nd->valid = FALSE;
+ nd = nd->next;
+ }
+
+ if (node->level == 0)
+ {
+ GtkTextLine *line;
+
+ line = node->children.line;
+ while (line != NULL)
+ {
+ GtkTextLineData *ld;
+
+ ld = line->views;
+ while (ld != NULL)
+ {
+ ld->valid = FALSE;
+ ld = ld->next;
+ }
+
+ line = line->next;
+ }
+ }
+ else
+ {
+ GtkTextBTreeNode *child;
+
+ child = node->children.node;
+
+ while (child != NULL)
+ {
+ gtk_text_btree_node_invalidate_downward(child);
+
+ child = child->next;
+ }
+ }
+}
+
+static void
+gtk_text_btree_node_invalidate_upward(GtkTextBTreeNode *node, gpointer view_id)
+{
+ GtkTextBTreeNode *iter;
+
+ iter = node;
+ while (iter != NULL)
+ {
+ NodeData *nd;
+
+ if (view_id)
+ {
+ nd = node_data_find (iter->node_data, view_id);
+
+ if (nd == NULL || !nd->valid)
+ break; /* Once a node is invalid, we know its parents are as well. */
+
+ nd->valid = FALSE;
+ }
+ else
+ {
+ gboolean should_continue = FALSE;
+
+ nd = iter->node_data;
+ while (nd != NULL)
+ {
+ if (nd->valid)
+ {
+ should_continue = TRUE;
+ nd->valid = FALSE;
+ }
+
+ nd = nd->next;
+ }
+
+ if (!should_continue)
+ break; /* This node was totally invalidated, so are its
+ parents */
+ }
+
+ iter = iter->parent;
+ }
+}
+
+
+/**
+ * gtk_text_btree_is_valid:
+ * @tree: a #GtkTextBTree
+ * @view_id: ID for the view
+ *
+ * Check to see if the entire #GtkTextBTree is valid or not for
+ * the given view.
+ *
+ * Return value: %TRUE if the entire #GtkTextBTree is valid
+ **/
+gboolean
+gtk_text_btree_is_valid (GtkTextBTree *tree,
+ gpointer view_id)
+{
+ NodeData *nd;
+ g_return_val_if_fail (tree != NULL, FALSE);
+
+ nd = node_data_find (tree->root_node->node_data, view_id);
+ return (nd && nd->valid);
+}
+
+typedef struct _ValidateState ValidateState;
+
+struct _ValidateState
+{
+ gint remaining_pixels;
+ gboolean in_validation;
+ gint y;
+ gint old_height;
+ gint new_height;
+};
+
+static void
+gtk_text_btree_node_validate (BTreeView *view,
+ GtkTextBTreeNode *node,
+ gpointer view_id,
+ ValidateState *state)
+{
+ gint node_valid = TRUE;
+ gint node_width = 0;
+ gint node_height = 0;
+
+ NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
+ g_return_if_fail (!nd->valid);
+
+ if (node->level == 0)
+ {
+ GtkTextLine *line = node->children.line;
+ GtkTextLineData *ld;
+
+ /* Iterate over leading valid lines */
+ while (line != NULL)
+ {
+ ld = gtk_text_line_get_data (line, view_id);
+
+ if (!ld || !ld->valid)
+ break;
+ else if (state->in_validation)
+ {
+ state->in_validation = FALSE;
+ return;
+ }
+ else
+ {
+ state->y += ld->height;
+ node_width = MAX (ld->width, node_width);
+ node_height += ld->height;
+ }
+
+ line = line->next;
+ }
+
+ state->in_validation = TRUE;
+
+ /* Iterate over invalid lines */
+ while (line != NULL)
+ {
+ ld = gtk_text_line_get_data (line, view_id);
+
+ if (ld && ld->valid)
+ break;
+ else
+ {
+ if (ld)
+ state->old_height += ld->height;
+ ld = gtk_text_layout_wrap (view->layout, line, ld);
+ state->new_height += ld->height;
+
+ node_width = MAX (ld->width, node_width);
+ node_height += ld->height;
+
+ state->remaining_pixels -= ld->height;
+ if (state->remaining_pixels <= 0)
+ {
+ line = line->next;
+ break;
+ }
+ }
+
+ line = line->next;
+ }
+
+ /* Iterate over the remaining lines */
+ while (line != NULL)
+ {
+ ld = gtk_text_line_get_data (line, view_id);
+ state->in_validation = FALSE;
+
+ if (!ld || !ld->valid)
+ node_valid = FALSE;
+
+ if (ld)
+ {
+ node_width = MAX (ld->width, node_width);
+ node_height += ld->height;
+ }
+
+ line = line->next;
+ }
+ }
+ else
+ {
+ GtkTextBTreeNode *child;
+ NodeData *child_nd;
+
+ child = node->children.node;
+
+ /* Iterate over leading valid nodes */
+ while (child)
+ {
+ child_nd = gtk_text_btree_node_ensure_data (child, view_id);
+
+ if (!child_nd->valid)
+ break;
+ else if (state->in_validation)
+ {
+ state->in_validation = FALSE;
+ return;
+ }
+ else
+ {
+ state->y += child_nd->height;
+ node_width = MAX (node_width, child_nd->width);
+ node_height += child_nd->height;
+ }
+
+ child = child->next;
+ }
+
+ /* Iterate over invalid nodes */
+ while (child)
+ {
+ child_nd = gtk_text_btree_node_ensure_data (child, view_id);
+
+ if (child_nd->valid)
+ break;
+ else
+ {
+ gtk_text_btree_node_validate (view, child, view_id, state);
+
+ if (!child_nd->valid)
+ node_valid = FALSE;
+ node_width = MAX (node_width, child_nd->width);
+ node_height += child_nd->height;
+
+ if (!state->in_validation || state->remaining_pixels <= 0)
+ {
+ child = child->next;
+ break;
+ }
+ }
+
+ child = child->next;
+ }
+
+ /* Iterate over the remaining lines */
+ while (child)
+ {
+ child_nd = gtk_text_btree_node_ensure_data (child, view_id);
+ state->in_validation = FALSE;
+
+ if (!child_nd->valid)
+ node_valid = FALSE;
+
+ node_width = MAX (child_nd->width, node_width);
+ node_height += child_nd->height;
+
+ child = child->next;
+ }
+ }
+
+ nd->width = node_width;
+ nd->height = node_height;
+ nd->valid = node_valid;
+}
+
+/**
+ * gtk_text_btree_validate:
+ * @tree: a #GtkTextBTree
+ * @view_id: view id
+ * @max_pixels: the maximum number of pixels to validate. (No more
+ * than one paragraph beyond this limit will be validated)
+ * @y: location to store starting y coordinate of validated region
+ * @old_height: location to store old height of validated region
+ * @new_height: location to store new height of validated region
+ *
+ * Validate a single contiguous invalid region of a #GtkTextBTree for
+ * a given view.
+ *
+ * Return value: %TRUE if a region has been validated, %FALSE if the
+ * entire tree was already valid.
+ **/
+gboolean
+gtk_text_btree_validate (GtkTextBTree *tree,
+ gpointer view_id,
+ gint max_pixels,
+ gint *y,
+ gint *old_height,
+ gint *new_height)
+{
+ BTreeView *view;
+
+ g_return_val_if_fail (tree != NULL, FALSE);
+
+ view = gtk_text_btree_get_view (tree, view_id);
+ g_return_val_if_fail (view != NULL, FALSE);
+
+ if (!gtk_text_btree_is_valid (tree, view_id))
+ {
+ ValidateState state;
+
+ state.remaining_pixels = max_pixels;
+ state.in_validation = FALSE;
+ state.y = 0;
+ state.old_height = 0;
+ state.new_height = 0;
+
+ gtk_text_btree_node_validate (view,
+ tree->root_node,
+ view_id, &state);
+
+ if (y)
+ *y = state.y;
+ if (old_height)
+ *old_height = state.old_height;
+ if (new_height)
+ *new_height = state.new_height;
+
+ if (gtk_debug_flags & GTK_DEBUG_TEXT)
+ gtk_text_btree_check(tree);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static void
+gtk_text_btree_node_compute_view_aggregates (GtkTextBTreeNode *node,
+ gpointer view_id,
+ gint *width_out,
+ gint *height_out,
+ gboolean *valid_out)
+{
+ gint width = 0;
+ gint height = 0;
+ gboolean valid = TRUE;
+
+ if (node->level == 0)
+ {
+ GtkTextLine *line = node->children.line;
+
+ while (line != NULL)
+ {
+ GtkTextLineData *ld = gtk_text_line_get_data (line, view_id);
+
+ if (!ld || !ld->valid)
+ valid = FALSE;
+
+ if (ld)
+ {
+ width = MAX (ld->width, width);
+ height += ld->height;
+ }
+
+ line = line->next;
+ }
+ }
+ else
+ {
+ GtkTextBTreeNode *child = node->children.node;
+
+ while (child)
+ {
+ NodeData *child_nd = node_data_find (child->node_data, view_id);
+
+ if (!child_nd || !child_nd->valid)
+ valid = FALSE;
+
+ if (child_nd)
+ {
+ width = MAX (child_nd->width, width);
+ height += child_nd->height;
+ }
+
+ child = child->next;
+ }
+ }
+
+ *width_out = width;
+ *height_out = height;
+ *valid_out = valid;
+}
+
+
+/* Recompute the validity and size of the view data for a given
+ * view at this node from the immediate children of the node
+ */
+static NodeData *
+gtk_text_btree_node_check_valid (GtkTextBTreeNode *node,
+ gpointer view_id)
+{
+ NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
+ gboolean valid;
+ gint width;
+ gint height;
+
+ gtk_text_btree_node_compute_view_aggregates (node, view_id,
+ &width, &height, &valid);
+ nd->width = width;
+ nd->height = height;
+ nd->valid = valid;
+
+ return nd;
+}
+
+static void
+gtk_text_btree_node_check_valid_upward (GtkTextBTreeNode *node,
+ gpointer view_id)
+{
+ while (node)
+ {
+ gtk_text_btree_node_check_valid (node, view_id);
+ node = node->parent;
+ }
+}
+
+static NodeData *
+gtk_text_btree_node_check_valid_downward (GtkTextBTreeNode *node,
+ gpointer view_id)
+{
+ if (node->level == 0)
+ {
+ return gtk_text_btree_node_check_valid (node, view_id);
+ }
+ else
+ {
+ GtkTextBTreeNode *child = node->children.node;
+
+ NodeData *nd = gtk_text_btree_node_ensure_data (node, view_id);
+
+ nd->valid = TRUE;
+ nd->width = 0;
+ nd->height = 0;
+
+ while (child)
+ {
+ NodeData *child_nd = gtk_text_btree_node_check_valid_downward (child, view_id);
+
+ if (!child_nd->valid)
+ nd->valid = FALSE;
+ nd->width = MAX (child_nd->width, nd->width);
+ nd->height += child_nd->height;
+
+ child = child->next;
+ }
+ return nd;
+ }
+}
+
+
+
+/**
+ * gtk_text_btree_validate_line:
+ * @tree: a #GtkTextBTree
+ * @line: line to validate
+ * @view_id: view ID for the view to validate
+ *
+ * Revalidate a single line of the btree for the given view, propagate
+ * results up through the entire tree.
+ **/
+void
+gtk_text_btree_validate_line (GtkTextBTree *tree,
+ GtkTextLine *line,
+ gpointer view_id)
+{
+ GtkTextLineData *ld;
+ GtkTextLine *last_line;
+ BTreeView *view;
+
+ g_return_if_fail (tree != NULL);
+ g_return_if_fail (line != NULL);
+
+ view = gtk_text_btree_get_view (tree, view_id);
+ g_return_if_fail (view != NULL);
+
+ ld = gtk_text_line_get_data(line, view_id);
+ if (!ld || !ld->valid)
+ {
+ ld = gtk_text_layout_wrap (view->layout, line, ld);
+ last_line = get_last_line (tree);
+
+ gtk_text_btree_node_check_valid_upward (line->parent, view_id);
+ }
+}
+
+static void
+gtk_text_btree_node_remove_view(BTreeView *view, GtkTextBTreeNode *node, gpointer view_id)
+{
+ if (node->level == 0)
+ {
+ GtkTextLine *line;
+
+ line = node->children.line;
+ while (line != NULL)
+ {
+ GtkTextLineData *ld;
+
+ ld = gtk_text_line_remove_data(line, view_id);
+
+ if (ld)
+ gtk_text_layout_free_line_data (view->layout, line, ld);
+
+ line = line->next;
+ }
+ }
+ else
+ {
+ GtkTextBTreeNode *child;
+
+ child = node->children.node;
+
+ while (child != NULL)
+ {
+ /* recurse */
+ gtk_text_btree_node_remove_view(view, child, view_id);
+
+ child = child->next;
+ }
+ }
+
+ gtk_text_btree_node_remove_data(node, view_id);
+}
+
+static void
+gtk_text_btree_node_destroy(GtkTextBTree *tree, GtkTextBTreeNode *node)
+{
+ if (node->level == 0)
+ {
+ GtkTextLine *line;
+ GtkTextLineSegment *seg;
+
+ while (node->children.line != NULL)
+ {
+ line = node->children.line;
+ node->children.line = line->next;
+ while (line->segments != NULL)
+ {
+ seg = line->segments;
+ line->segments = seg->next;
+ (*seg->type->deleteFunc)(seg, line, 1);
+ }
+ gtk_text_line_destroy(tree, line);
+ }
+ }
+ else
+ {
+ GtkTextBTreeNode *childPtr;
+
+ while (node->children.node != NULL)
+ {
+ childPtr = node->children.node;
+ node->children.node = childPtr->next;
+ gtk_text_btree_node_destroy(tree, childPtr);
+ }
+ }
+ summary_list_destroy(node->summary);
+ node_data_list_destroy(node->node_data);
+ g_free(node);
+}
+
+static NodeData*
+gtk_text_btree_node_ensure_data(GtkTextBTreeNode *node, gpointer view_id)
+{
+ NodeData *nd;
+
+ nd = node->node_data;
+ while (nd != NULL)
+ {
+ if (nd->view_id == view_id)
+ break;
+
+ nd = nd->next;
+ }
+
+ if (nd == NULL) {
+ nd = node_data_new(view_id);
+
+ if (node->node_data)
+ nd->next = node->node_data;
+
+ node->node_data = nd;
+ }
+
+ return nd;
+}
+
+static void
+gtk_text_btree_node_remove_data(GtkTextBTreeNode *node, gpointer view_id)
+{
+ NodeData *nd;
+ NodeData *prev;
+
+ prev = NULL;
+ nd = node->node_data;
+ while (nd != NULL)
+ {
+ if (nd->view_id == view_id)
+ break;
+
+ nd = nd->next;
+ }
+
+ if (nd == NULL)
+ return;
+
+ if (prev != NULL)
+ prev->next = nd->next;
+
+ if (node->node_data == nd)
+ node->node_data = nd->next;
+
+ nd->next = NULL;
+
+ node_data_destroy(nd);
+}
+
+static void
+gtk_text_btree_node_get_size(GtkTextBTreeNode *node, gpointer view_id,
+ gint *width, gint *height)
+{
+ NodeData *nd;
+
+ g_return_if_fail(width != NULL);
+ g_return_if_fail(height != NULL);
+
+ nd = gtk_text_btree_node_ensure_data(node, view_id);
+
+ if (width)
+ *width = nd->width;
+ if (height)
+ *height = nd->height;
+}
+
+/* Find the closest common ancestor of the two nodes. FIXME: The interface
+ * here isn't quite right, since for a lot of operations we want to
+ * know which children of the common parent correspond to the two nodes
+ * (e.g., when computing the order of two iters)
+ */
+static GtkTextBTreeNode *
+gtk_text_btree_node_common_parent (GtkTextBTreeNode *node1,
+ GtkTextBTreeNode *node2)
+{
+ while (node1->level < node2->level)
+ node1 = node1->parent;
+ while (node2->level < node1->level)
+ node2 = node2->parent;
+ while (node1 != node2)
+ {
+ node1 = node1->parent;
+ node2 = node2->parent;
+ }
+
+ return node1;
+}
+
+/*
+ * BTree
+ */
+
+static BTreeView*
+gtk_text_btree_get_view(GtkTextBTree *tree, gpointer view_id)
+{
+ BTreeView *view;
+
+ view = tree->views;
+ while (view != NULL)
+ {
+ if (view->view_id == view_id)
+ break;
+ view = view->next;
+ }
+
+ return view;
+}
+
+static void
+get_tree_bounds(GtkTextBTree *tree,
+ GtkTextIter *start,
+ GtkTextIter *end)
+{
+ gtk_text_btree_get_iter_at_line_char(tree, start, 0, 0);
+ gtk_text_btree_get_last_iter(tree, end);
+}
+
+static void
+tag_changed_cb(GtkTextTagTable *table,
+ GtkTextTag *tag,
+ gboolean size_changed,
+ GtkTextBTree *tree)
+{
+ if (size_changed)
+ {
+ /* We need to queue a redisplay on all regions that are tagged with
+ this tag. */
+
+ GtkTextIter start;
+ GtkTextIter end;
+
+ if (gtk_text_btree_get_iter_at_first_toggle(tree, &start, tag))
+ {
+ /* Must be a last toggle if there was a first one. */
+ gtk_text_btree_get_iter_at_last_toggle(tree, &end, tag);
+ gtk_text_btree_invalidate_region(tree,
+ &start, &end);
+
+ }
+ }
+ else
+ {
+ BTreeView *view;
+
+ view = tree->views;
+
+ while (view != NULL)
+ {
+ gint width, height;
+
+ gtk_text_btree_get_view_size (tree, view->view_id, &width, &height);
+ gtk_text_layout_changed (view->layout, 0, height, height);
+
+ view = view->next;
+ }
+ }
+}
+
+static void
+tag_removed_cb(GtkTextTagTable *table,
+ GtkTextTag *tag,
+ GtkTextBTree *tree)
+{
+ /* Remove the tag from the tree */
+
+ GtkTextIter start;
+ GtkTextIter end;
+
+ get_tree_bounds(tree, &start, &end);
+
+ gtk_text_btree_tag(&start, &end, tag, FALSE);
+ gtk_text_btree_remove_tag_info (tree, tag);
+}
+
+
+/* Rebalance the out-of-whack node "node" */
+static void
+gtk_text_btree_rebalance(GtkTextBTree *tree,
+ GtkTextBTreeNode *node)
+{
+ /*
+ * Loop over the entire ancestral chain of the GtkTextBTreeNode, working
+ * up through the tree one GtkTextBTreeNode at a time until the root
+ * GtkTextBTreeNode has been processed.
+ */
+
+ while (node != NULL)
+ {
+ GtkTextBTreeNode *new_node, *child;
+ GtkTextLine *line;
+ int i;
+
+ /*
+ * Check to see if the GtkTextBTreeNode has too many children. If it does,
+ * then split off all but the first MIN_CHILDREN into a separate
+ * GtkTextBTreeNode following the original one. Then repeat until the
+ * GtkTextBTreeNode has a decent size.
+ */
+
+ if (node->num_children > MAX_CHILDREN)
+ {
+ while (1)
+ {
+ /*
+ * If the GtkTextBTreeNode being split is the root
+ * GtkTextBTreeNode, then make a new root GtkTextBTreeNode above
+ * it first.
+ */
+
+ if (node->parent == NULL)
+ {
+ new_node = gtk_text_btree_node_new();
+ new_node->parent = NULL;
+ new_node->next = NULL;
+ new_node->summary = NULL;
+ new_node->level = node->level + 1;
+ new_node->children.node = node;
+ recompute_node_counts(tree, new_node);
+ tree->root_node = new_node;
+ }
+ new_node = gtk_text_btree_node_new();
+ new_node->parent = node->parent;
+ new_node->next = node->next;
+ node->next = new_node;
+ new_node->summary = NULL;
+ new_node->level = node->level;
+ new_node->num_children = node->num_children - MIN_CHILDREN;
+ if (node->level == 0)
+ {
+ for (i = MIN_CHILDREN-1,
+ line = node->children.line;
+ i > 0; i--, line = line->next)
+ {
+ /* Empty loop body. */
+ }
+ new_node->children.line = line->next;
+ line->next = NULL;
+ }
+ else
+ {
+ for (i = MIN_CHILDREN-1,
+ child = node->children.node;
+ i > 0; i--, child = child->next)
+ {
+ /* Empty loop body. */
+ }
+ new_node->children.node = child->next;
+ child->next = NULL;
+ }
+ recompute_node_counts(tree, node);
+ node->parent->num_children++;
+ node = new_node;
+ if (node->num_children <= MAX_CHILDREN)
+ {
+ recompute_node_counts(tree, node);
+ break;
+ }
+ }
+ }
+
+ while (node->num_children < MIN_CHILDREN)
+ {
+ GtkTextBTreeNode *other;
+ GtkTextBTreeNode *halfwaynode = NULL; /* Initialization needed only */
+ GtkTextLine *halfwayline = NULL; /* to prevent cc warnings. */
+ int total_children, first_children, i;
+
+ /*
+ * Too few children for this GtkTextBTreeNode. If this is the root then,
+ * it's OK for it to have less than MIN_CHILDREN children
+ * as long as it's got at least two. If it has only one
+ * (and isn't at level 0), then chop the root GtkTextBTreeNode out of
+ * the tree and use its child as the new root.
+ */
+
+ if (node->parent == NULL)
+ {
+ if ((node->num_children == 1) && (node->level > 0))
+ {
+ tree->root_node = node->children.node;
+ tree->root_node->parent = NULL;
+ summary_list_destroy(node->summary);
+ g_free(node);
+ }
+ return;
+ }
+
+ /*
+ * Not the root. Make sure that there are siblings to
+ * balance with.
+ */
+
+ if (node->parent->num_children < 2)
+ {
+ gtk_text_btree_rebalance(tree, node->parent);
+ continue;
+ }
+
+ /*
+ * Find a sibling neighbor to borrow from, and arrange for
+ * node to be the earlier of the pair.
+ */
+
+ if (node->next == NULL)
+ {
+ for (other = node->parent->children.node;
+ other->next != node;
+ other = other->next)
+ {
+ /* Empty loop body. */
+ }
+ node = other;
+ }
+ other = node->next;
+
+ /*
+ * We're going to either merge the two siblings together
+ * into one GtkTextBTreeNode or redivide the children among them to
+ * balance their loads. As preparation, join their two
+ * child lists into a single list and remember the half-way
+ * point in the list.
+ */
+
+ total_children = node->num_children + other->num_children;
+ first_children = total_children/2;
+ if (node->children.node == NULL)
+ {
+ node->children = other->children;
+ other->children.node = NULL;
+ other->children.line = NULL;
+ }
+ if (node->level == 0)
+ {
+ GtkTextLine *line;
+
+ for (line = node->children.line, i = 1;
+ line->next != NULL;
+ line = line->next, i++)
+ {
+ if (i == first_children)
+ {
+ halfwayline = line;
+ }
+ }
+ line->next = other->children.line;
+ while (i <= first_children)
+ {
+ halfwayline = line;
+ line = line->next;
+ i++;
+ }
+ }
+ else
+ {
+ GtkTextBTreeNode *child;
+
+ for (child = node->children.node, i = 1;
+ child->next != NULL;
+ child = child->next, i++)
+ {
+ if (i <= first_children)
+ {
+ if (i == first_children)
+ {
+ halfwaynode = child;
+ }
+ }
+ }
+ child->next = other->children.node;
+ while (i <= first_children)
+ {
+ halfwaynode = child;
+ child = child->next;
+ i++;
+ }
+ }
+
+ /*
+ * If the two siblings can simply be merged together, do it.
+ */
+
+ if (total_children <= MAX_CHILDREN)
+ {
+ recompute_node_counts(tree, node);
+ node->next = other->next;
+ node->parent->num_children--;
+ summary_list_destroy(other->summary);
+ g_free(other);
+ continue;
+ }
+
+ /*
+ * The siblings can't be merged, so just divide their
+ * children evenly between them.
+ */
+
+ if (node->level == 0)
+ {
+ other->children.line = halfwayline->next;
+ halfwayline->next = NULL;
+ }
+ else
+ {
+ other->children.node = halfwaynode->next;
+ halfwaynode->next = NULL;
+ }
+
+ recompute_node_counts(tree, node);
+ recompute_node_counts(tree, other);
+ }
+
+ node = node->parent;
+ }
+}
+
+static void
+post_insert_fixup(GtkTextBTree *tree,
+ GtkTextLine *line,
+ gint line_count_delta,
+ gint char_count_delta)
+
+{
+ GtkTextBTreeNode *node;
+
+ /*
+ * Increment the line counts in all the parent GtkTextBTreeNodes of the insertion
+ * point, then rebalance the tree if necessary.
+ */
+
+ for (node = line->parent ; node != NULL;
+ node = node->parent)
+ {
+ node->num_lines += line_count_delta;
+ node->num_chars += char_count_delta;
+ }
+ node = line->parent;
+ node->num_children += line_count_delta;
+
+ if (node->num_children > MAX_CHILDREN)
+ {
+ gtk_text_btree_rebalance(tree, node);
+ }
+
+ if (gtk_debug_flags & GTK_DEBUG_TEXT)
+ gtk_text_btree_check(tree);
+}
+
+static GtkTextTagInfo*
+gtk_text_btree_get_existing_tag_info(GtkTextBTree *tree,
+ GtkTextTag *tag)
+{
+ GtkTextTagInfo *info;
+ GSList *list;
+
+
+ list = tree->tag_infos;
+ while (list != NULL)
+ {
+ info = list->data;
+ if (info->tag == tag)
+ return info;
+
+ list = g_slist_next(list);
+ }
+
+ return NULL;
+}
+
+static GtkTextTagInfo*
+gtk_text_btree_get_tag_info(GtkTextBTree *tree,
+ GtkTextTag *tag)
+{
+ GtkTextTagInfo *info;
+
+ info = gtk_text_btree_get_existing_tag_info(tree, tag);
+
+ if (info == NULL)
+ {
+ /* didn't find it, create. */
+
+ info = g_new(GtkTextTagInfo, 1);
+
+ info->tag = tag;
+ gtk_object_ref(GTK_OBJECT(tag));
+ info->tag_root = NULL;
+ info->toggle_count = 0;
+
+ tree->tag_infos = g_slist_prepend(tree->tag_infos, info);
+ }
+
+ return info;
+}
+
+static void
+gtk_text_btree_remove_tag_info(GtkTextBTree *tree,
+ GtkTextTag *tag)
+{
+ GtkTextTagInfo *info;
+ GSList *list;
+ GSList *prev;
+
+ prev = NULL;
+ list = tree->tag_infos;
+ while (list != NULL)
+ {
+ info = list->data;
+ if (info->tag == tag)
+ {
+ if (prev != NULL)
+ {
+ prev->next = list->next;
+ }
+ else
+ {
+ tree->tag_infos = list->next;
+ }
+ list->next = NULL;
+ g_slist_free(list);
+
+ gtk_object_unref(GTK_OBJECT(info->tag));
+
+ g_free(info);
+ return;
+ }
+
+ list = g_slist_next(list);
+ }
+
+ g_assert_not_reached();
+ return;
+}
+
+static void
+recompute_level_zero_counts(GtkTextBTreeNode *node)
+{
+ GtkTextLine *line;
+ GtkTextLineSegment *seg;
+
+ g_assert(node->level == 0);
+
+ line = node->children.line;
+ while (line != NULL)
+ {
+ node->num_children++;
+ node->num_lines++;
+
+ if (line->parent != node)
+ gtk_text_line_set_parent(line, node);
+
+ seg = line->segments;
+ while (seg != NULL)
+ {
+
+ node->num_chars += seg->char_count;
+
+ if (((seg->type != &gtk_text_toggle_on_type)
+ && (seg->type != &gtk_text_toggle_off_type))
+ || !(seg->body.toggle.inNodeCounts))
+ {
+ ; /* nothing */
+ }
+ else
+ {
+ GtkTextTagInfo *info;
+
+ info = seg->body.toggle.info;
+
+ gtk_text_btree_node_adjust_toggle_count(node, info, 1);
+ }
+
+ seg = seg->next;
+ }
+
+ line = line->next;
+ }
+}
+
+static void
+recompute_level_nonzero_counts(GtkTextBTreeNode *node)
+{
+ Summary *summary;
+ GtkTextBTreeNode *child;
+
+ g_assert(node->level > 0);
+
+ child = node->children.node;
+ while (child != NULL)
+ {
+ node->num_children += 1;
+ node->num_lines += child->num_lines;
+ node->num_chars += child->num_chars;
+
+ if (child->parent != node)
+ {
+ child->parent = node;
+ gtk_text_btree_node_invalidate_upward(node, NULL);
+ }
+
+ summary = child->summary;
+ while (summary != NULL)
+ {
+ gtk_text_btree_node_adjust_toggle_count(node,
+ summary->info,
+ summary->toggle_count);
+
+ summary = summary->next;
+ }
+
+ child = child->next;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * recompute_node_counts --
+ *
+ * This procedure is called to recompute all the counts in a GtkTextBTreeNode
+ * (tags, child information, etc.) by scanning the information in
+ * its descendants. This procedure is called during rebalancing
+ * when a GtkTextBTreeNode's child structure has changed.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The tag counts for node are modified to reflect its current
+ * child structure, as are its num_children, num_lines, num_chars fields.
+ * Also, all of the childrens' parent fields are made to point
+ * to node.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+recompute_node_counts(GtkTextBTree *tree, GtkTextBTreeNode *node)
+{
+ BTreeView *view;
+ Summary *summary, *summary2;
+
+ /*
+ * Zero out all the existing counts for the GtkTextBTreeNode, but don't delete
+ * the existing Summary records (most of them will probably be reused).
+ */
+
+ summary = node->summary;
+ while (summary != NULL)
+ {
+ summary->toggle_count = 0;
+ summary = summary->next;
+ }
+
+ node->num_children = 0;
+ node->num_lines = 0;
+ node->num_chars = 0;
+
+ /*
+ * Scan through the children, adding the childrens' tag counts into
+ * the GtkTextBTreeNode's tag counts and adding new Summary structures if
+ * necessary.
+ */
+
+ if (node->level == 0)
+ recompute_level_zero_counts(node);
+ else
+ recompute_level_nonzero_counts(node);
+
+ view = tree->views;
+ while (view)
+ {
+ gtk_text_btree_node_check_valid (node, view->view_id);
+ view = view->next;
+ }
+
+ /*
+ * Scan through the GtkTextBTreeNode's tag records again and delete any Summary
+ * records that still have a zero count, or that have all the toggles.
+ * The GtkTextBTreeNode with the children that account for all the tags toggles
+ * have no summary information, and they become the tag_root for the tag.
+ */
+
+ summary2 = NULL;
+ for (summary = node->summary; summary != NULL; )
+ {
+ if (summary->toggle_count > 0 &&
+ summary->toggle_count < summary->info->toggle_count)
+ {
+ if (node->level == summary->info->tag_root->level)
+ {
+ /*
+ * The tag's root GtkTextBTreeNode split and some toggles left.
+ * The tag root must move up a level.
+ */
+ summary->info->tag_root = node->parent;
+ }
+ summary2 = summary;
+ summary = summary->next;
+ continue;
+ }
+ if (summary->toggle_count == summary->info->toggle_count)
+ {
+ /*
+ * A GtkTextBTreeNode merge has collected all the toggles under
+ * one GtkTextBTreeNode. Push the root down to this level.
+ */
+ summary->info->tag_root = node;
+ }
+ if (summary2 != NULL)
+ {
+ summary2->next = summary->next;
+ summary_destroy (summary);
+ summary = summary2->next;
+ }
+ else
+ {
+ node->summary = summary->next;
+ summary_destroy (summary);
+ summary = node->summary;
+ }
+ }
+}
+
+void
+change_node_toggle_count(GtkTextBTreeNode *node,
+ GtkTextTagInfo *info,
+ gint delta) /* may be negative */
+{
+ Summary *summary, *prevPtr;
+ GtkTextBTreeNode *node2Ptr;
+ int rootLevel; /* Level of original tag root */
+
+ info->toggle_count += delta;
+
+ if (info->tag_root == (GtkTextBTreeNode *) NULL)
+ {
+ info->tag_root = node;
+ return;
+ }
+
+ /*
+ * Note the level of the existing root for the tag so we can detect
+ * if it needs to be moved because of the toggle count change.
+ */
+
+ rootLevel = info->tag_root->level;
+
+ /*
+ * Iterate over the GtkTextBTreeNode and its ancestors up to the tag root, adjusting
+ * summary counts at each GtkTextBTreeNode and moving the tag's root upwards if
+ * necessary.
+ */
+
+ for ( ; node != info->tag_root; node = node->parent)
+ {
+ /*
+ * See if there's already an entry for this tag for this GtkTextBTreeNode. If so,
+ * perhaps all we have to do is adjust its count.
+ */
+
+ for (prevPtr = NULL, summary = node->summary;
+ summary != NULL;
+ prevPtr = summary, summary = summary->next)
+ {
+ if (summary->info == info)
+ {
+ break;
+ }
+ }
+ if (summary != NULL)
+ {
+ summary->toggle_count += delta;
+ if (summary->toggle_count > 0 &&
+ summary->toggle_count < info->toggle_count)
+ {
+ continue;
+ }
+ if (summary->toggle_count != 0)
+ {
+ /*
+ * Should never find a GtkTextBTreeNode with max toggle count at this
+ * point (there shouldn't have been a summary entry in the
+ * first place).
+ */
+
+ g_error("change_node_toggle_count: bad toggle count (%d) max (%d)",
+ summary->toggle_count, info->toggle_count);
+ }
+
+ /*
+ * Zero toggle count; must remove this tag from the list.
+ */
+
+ if (prevPtr == NULL)
+ {
+ node->summary = summary->next;
+ }
+ else
+ {
+ prevPtr->next = summary->next;
+ }
+ summary_destroy (summary);
+ }
+ else
+ {
+ /*
+ * This tag isn't currently in the summary information list.
+ */
+
+ if (rootLevel == node->level)
+ {
+
+ /*
+ * The old tag root is at the same level in the tree as this
+ * GtkTextBTreeNode, but it isn't at this GtkTextBTreeNode. Move the tag root up
+ * a level, in the hopes that it will now cover this GtkTextBTreeNode
+ * as well as the old root (if not, we'll move it up again
+ * the next time through the loop). To push it up one level
+ * we copy the original toggle count into the summary
+ * information at the old root and change the root to its
+ * parent GtkTextBTreeNode.
+ */
+
+ GtkTextBTreeNode *rootnode = info->tag_root;
+ summary = (Summary *) g_malloc(sizeof(Summary));
+ summary->info = info;
+ summary->toggle_count = info->toggle_count - delta;
+ summary->next = rootnode->summary;
+ rootnode->summary = summary;
+ rootnode = rootnode->parent;
+ rootLevel = rootnode->level;
+ info->tag_root = rootnode;
+ }
+ summary = (Summary *) g_malloc(sizeof(Summary));
+ summary->info = info;
+ summary->toggle_count = delta;
+ summary->next = node->summary;
+ node->summary = summary;
+ }
+ }
+
+ /*
+ * If we've decremented the toggle count, then it may be necessary
+ * to push the tag root down one or more levels.
+ */
+
+ if (delta >= 0)
+ {
+ return;
+ }
+ if (info->toggle_count == 0)
+ {
+ info->tag_root = (GtkTextBTreeNode *) NULL;
+ return;
+ }
+ node = info->tag_root;
+ while (node->level > 0)
+ {
+ /*
+ * See if a single child GtkTextBTreeNode accounts for all of the tag's
+ * toggles. If so, push the root down one level.
+ */
+
+ for (node2Ptr = node->children.node;
+ node2Ptr != (GtkTextBTreeNode *)NULL ;
+ node2Ptr = node2Ptr->next)
+ {
+ for (prevPtr = NULL, summary = node2Ptr->summary;
+ summary != NULL;
+ prevPtr = summary, summary = summary->next)
+ {
+ if (summary->info == info)
+ {
+ break;
+ }
+ }
+ if (summary == NULL)
+ {
+ continue;
+ }
+ if (summary->toggle_count != info->toggle_count)
+ {
+ /*
+ * No GtkTextBTreeNode has all toggles, so the root is still valid.
+ */
+
+ return;
+ }
+
+ /*
+ * This GtkTextBTreeNode has all the toggles, so push down the root.
+ */
+
+ if (prevPtr == NULL)
+ {
+ node2Ptr->summary = summary->next;
+ }
+ else
+ {
+ prevPtr->next = summary->next;
+ }
+ summary_destroy (summary);
+ info->tag_root = node2Ptr;
+ break;
+ }
+ node = info->tag_root;
+ }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * inc_count --
+ *
+ * This is a utility procedure used by gtk_text_btree_get_tags. It
+ * increments the count for a particular tag, adding a new
+ * entry for that tag if there wasn't one previously.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The information at *tagInfoPtr may be modified, and the arrays
+ * may be reallocated to make them larger.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+inc_count(GtkTextTag *tag, int inc, TagInfo *tagInfoPtr)
+{
+ GtkTextTag **tag_p;
+ int count;
+
+ for (tag_p = tagInfoPtr->tags, count = tagInfoPtr->numTags;
+ count > 0; tag_p++, count--)
+ {
+ if (*tag_p == tag)
+ {
+ tagInfoPtr->counts[tagInfoPtr->numTags-count] += inc;
+ return;
+ }
+ }
+
+ /*
+ * There isn't currently an entry for this tag, so we have to
+ * make a new one. If the arrays are full, then enlarge the
+ * arrays first.
+ */
+
+ if (tagInfoPtr->numTags == tagInfoPtr->arraySize)
+ {
+ GtkTextTag **newTags;
+ int *newCounts, newSize;
+
+ newSize = 2*tagInfoPtr->arraySize;
+ newTags = (GtkTextTag **) g_malloc((unsigned)
+ (newSize*sizeof(GtkTextTag *)));
+ memcpy((void *) newTags, (void *) tagInfoPtr->tags,
+ tagInfoPtr->arraySize *sizeof(GtkTextTag *));
+ g_free((char *) tagInfoPtr->tags);
+ tagInfoPtr->tags = newTags;
+ newCounts = (int *) g_malloc((unsigned) (newSize*sizeof(int)));
+ memcpy((void *) newCounts, (void *) tagInfoPtr->counts,
+ tagInfoPtr->arraySize *sizeof(int));
+ g_free((char *) tagInfoPtr->counts);
+ tagInfoPtr->counts = newCounts;
+ tagInfoPtr->arraySize = newSize;
+ }
+
+ tagInfoPtr->tags[tagInfoPtr->numTags] = tag;
+ tagInfoPtr->counts[tagInfoPtr->numTags] = inc;
+ tagInfoPtr->numTags++;
+}
+
+static void
+gtk_text_btree_link_segment(GtkTextLineSegment *seg,
+ const GtkTextIter *iter)
+{
+ GtkTextLineSegment *prev;
+ GtkTextLine *line;
+ GtkTextBTree *tree;
+
+ line = gtk_text_iter_get_line(iter);
+ tree = gtk_text_iter_get_btree(iter);
+
+ prev = gtk_text_line_segment_split(iter);
+ if (prev == NULL)
+ {
+ seg->next = line->segments;
+ line->segments = seg;
+ }
+ else
+ {
+ seg->next = prev->next;
+ prev->next = seg;
+ }
+ cleanup_line(line);
+ segments_changed(tree);
+
+ if (gtk_debug_flags & GTK_DEBUG_TEXT)
+ gtk_text_btree_check(tree);
+}
+
+static void
+gtk_text_btree_unlink_segment(GtkTextBTree *tree,
+ GtkTextLineSegment *seg,
+ GtkTextLine *line)
+{
+ GtkTextLineSegment *prev;
+
+ if (line->segments == seg)
+ {
+ line->segments = seg->next;
+ }
+ else
+ {
+ for (prev = line->segments; prev->next != seg;
+ prev = prev->next)
+ {
+ /* Empty loop body. */
+ }
+ prev->next = seg->next;
+ }
+ cleanup_line(line);
+ segments_changed(tree);
+}
+
+/*
+ * This is here because it requires BTree internals, it logically
+ * belongs in gtktextsegment.c
+ */
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * toggle_segment_check_func --
+ *
+ * This procedure is invoked to perform consistency checks
+ * on toggle segments.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If a consistency problem is found the procedure g_errors.
+ *
+ *--------------------------------------------------------------
+ */
+
+void
+toggle_segment_check_func(GtkTextLineSegment *segPtr,
+ GtkTextLine *line)
+{
+ Summary *summary;
+ int needSummary;
+
+ if (segPtr->byte_count != 0)
+ {
+ g_error("toggle_segment_check_func: segment had non-zero size");
+ }
+ if (!segPtr->body.toggle.inNodeCounts)
+ {
+ g_error("toggle_segment_check_func: toggle counts not updated in GtkTextBTreeNodes");
+ }
+ needSummary = (segPtr->body.toggle.info->tag_root != line->parent);
+ for (summary = line->parent->summary; ;
+ summary = summary->next)
+ {
+ if (summary == NULL)
+ {
+ if (needSummary)
+ {
+ g_error("toggle_segment_check_func: tag not present in GtkTextBTreeNode");
+ }
+ else
+ {
+ break;
+ }
+ }
+ if (summary->info == segPtr->body.toggle.info)
+ {
+ if (!needSummary)
+ {
+ g_error("toggle_segment_check_func: tag present in root GtkTextBTreeNode summary");
+ }
+ break;
+ }
+ }
+}
+
+/*
+ * Debug
+ */
+
+static void
+gtk_text_btree_node_view_check_consistency (GtkTextBTreeNode *node,
+ NodeData *nd)
+{
+ gint width;
+ gint height;
+ gboolean valid;
+
+ gtk_text_btree_node_compute_view_aggregates (node, nd->view_id,
+ &width, &height, &valid);
+ if (nd->width != width ||
+ nd->height != height ||
+ !nd->valid != !valid)
+ {
+ g_error ("Node aggregates for view %p are invalid:\n"
+ "Are (%d,%d,%s), should be (%d,%d,%s)",
+ nd->view_id,
+ nd->width, nd->height, nd->valid ? "TRUE" : "FALSE",
+ width, height, valid ? "TRUE" : "FALSE");
+ }
+}
+
+static void
+gtk_text_btree_node_check_consistency(GtkTextBTreeNode *node)
+{
+ GtkTextBTreeNode *childnode;
+ Summary *summary, *summary2;
+ GtkTextLine *line;
+ GtkTextLineSegment *segPtr;
+ int num_children, num_lines, num_chars, toggle_count, min_children;
+ GtkTextLineData *ld;
+ NodeData *nd;
+
+ if (node->parent != NULL)
+ {
+ min_children = MIN_CHILDREN;
+ }
+ else if (node->level > 0)
+ {
+ min_children = 2;
+ }
+ else {
+ min_children = 1;
+ }
+ if ((node->num_children < min_children)
+ || (node->num_children > MAX_CHILDREN))
+ {
+ g_error("gtk_text_btree_node_check_consistency: bad child count (%d)",
+ node->num_children);
+ }
+
+ nd = node->node_data;
+ while (nd != NULL)
+ {
+ gtk_text_btree_node_view_check_consistency (node, nd);
+ nd = nd->next;
+ }
+
+ num_children = 0;
+ num_lines = 0;
+ num_chars = 0;
+ if (node->level == 0)
+ {
+ for (line = node->children.line; line != NULL;
+ line = line->next)
+ {
+ if (line->parent != node)
+ {
+ g_error("gtk_text_btree_node_check_consistency: line doesn't point to parent");
+ }
+ if (line->segments == NULL)
+ {
+ g_error("gtk_text_btree_node_check_consistency: line has no segments");
+ }
+
+ ld = line->views;
+ while (ld != NULL)
+ {
+ /* Just ensuring we don't segv while doing this loop */
+
+ ld = ld->next;
+ }
+
+ for (segPtr = line->segments; segPtr != NULL; segPtr = segPtr->next)
+ {
+ if (segPtr->type->checkFunc != NULL)
+ {
+ (*segPtr->type->checkFunc)(segPtr, line);
+ }
+ if ((segPtr->byte_count == 0) && (!segPtr->type->leftGravity)
+ && (segPtr->next != NULL)
+ && (segPtr->next->byte_count == 0)
+ && (segPtr->next->type->leftGravity))
+ {
+ g_error("gtk_text_btree_node_check_consistency: wrong segment order for gravity");
+ }
+ if ((segPtr->next == NULL)
+ && (segPtr->type != &gtk_text_char_type))
+ {
+ g_error("gtk_text_btree_node_check_consistency: line ended with wrong type");
+ }
+
+ num_chars += segPtr->char_count;
+ }
+
+ num_children++;
+ num_lines++;
+ }
+ }
+ else
+ {
+ for (childnode = node->children.node; childnode != NULL;
+ childnode = childnode->next)
+ {
+ if (childnode->parent != node)
+ {
+ g_error("gtk_text_btree_node_check_consistency: GtkTextBTreeNode doesn't point to parent");
+ }
+ if (childnode->level != (node->level-1))
+ {
+ g_error("gtk_text_btree_node_check_consistency: level mismatch (%d %d)",
+ node->level, childnode->level);
+ }
+ gtk_text_btree_node_check_consistency (childnode);
+ for (summary = childnode->summary; summary != NULL;
+ summary = summary->next)
+ {
+ for (summary2 = node->summary; ;
+ summary2 = summary2->next)
+ {
+ if (summary2 == NULL)
+ {
+ if (summary->info->tag_root == node)
+ {
+ break;
+ }
+ g_error("gtk_text_btree_node_check_consistency: GtkTextBTreeNode tag \"%s\" not %s",
+ summary->info->tag->name,
+ "present in parent summaries");
+ }
+ if (summary->info == summary2->info)
+ {
+ break;
+ }
+ }
+ }
+ num_children++;
+ num_lines += childnode->num_lines;
+ num_chars += childnode->num_chars;
+ }
+ }
+ if (num_children != node->num_children)
+ {
+ g_error("gtk_text_btree_node_check_consistency: mismatch in num_children (%d %d)",
+ num_children, node->num_children);
+ }
+ if (num_lines != node->num_lines)
+ {
+ g_error("gtk_text_btree_node_check_consistency: mismatch in num_lines (%d %d)",
+ num_lines, node->num_lines);
+ }
+ if (num_chars != node->num_chars)
+ {
+ g_error("%s: mismatch in num_chars (%d %d)",
+ __FUNCTION__, num_chars, node->num_chars);
+ }
+
+ for (summary = node->summary; summary != NULL;
+ summary = summary->next)
+ {
+ if (summary->info->toggle_count == summary->toggle_count)
+ {
+ g_error("gtk_text_btree_node_check_consistency: found unpruned root for \"%s\"",
+ summary->info->tag->name);
+ }
+ toggle_count = 0;
+ if (node->level == 0)
+ {
+ for (line = node->children.line; line != NULL;
+ line = line->next)
+ {
+ for (segPtr = line->segments; segPtr != NULL;
+ segPtr = segPtr->next)
+ {
+ if ((segPtr->type != &gtk_text_toggle_on_type)
+ && (segPtr->type != &gtk_text_toggle_off_type))
+ {
+ continue;
+ }
+ if (segPtr->body.toggle.info == summary->info)
+ {
+ if (!segPtr->body.toggle.inNodeCounts)
+ g_error ("Toggle segment not in the node counts");
+
+ toggle_count ++;
+ }
+ }
+ }
+ }
+ else
+ {
+ for (childnode = node->children.node;
+ childnode != NULL;
+ childnode = childnode->next)
+ {
+ for (summary2 = childnode->summary;
+ summary2 != NULL;
+ summary2 = summary2->next)
+ {
+ if (summary2->info == summary->info)
+ {
+ toggle_count += summary2->toggle_count;
+ }
+ }
+ }
+ }
+ if (toggle_count != summary->toggle_count)
+ {
+ g_error("gtk_text_btree_node_check_consistency: mismatch in toggle_count (%d %d)",
+ toggle_count, summary->toggle_count);
+ }
+ for (summary2 = summary->next; summary2 != NULL;
+ summary2 = summary2->next)
+ {
+ if (summary2->info == summary->info)
+ {
+ g_error("gtk_text_btree_node_check_consistency: duplicated GtkTextBTreeNode tag: %s",
+ summary->info->tag->name);
+ }
+ }
+ }
+}
+
+static void
+listify_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+ GSList** listp = user_data;
+
+ *listp = g_slist_prepend(*listp, value);
+}
+
+static GSList*
+list_of_tags(GtkTextTagTable *table)
+{
+ GSList *list = NULL;
+
+ gtk_text_tag_table_foreach(table, listify_foreach, &list);
+
+ return list;
+}
+
+void
+gtk_text_btree_check (GtkTextBTree *tree)
+{
+ Summary *summary;
+ GtkTextBTreeNode *node;
+ GtkTextLine *line;
+ GtkTextLineSegment *seg;
+ GtkTextTag *tag;
+ GSList *taglist = NULL;
+ int count;
+ GtkTextTagInfo *info;
+
+ /*
+ * Make sure that the tag toggle counts and the tag root pointers are OK.
+ */
+ for (taglist = list_of_tags(tree->table);
+ taglist != NULL ; taglist = taglist->next)
+ {
+ tag = taglist->data;
+ info = gtk_text_btree_get_existing_tag_info(tree, tag);
+ if (info != NULL)
+ {
+ node = info->tag_root;
+ if (node == NULL)
+ {
+ if (info->toggle_count != 0)
+ {
+ g_error("gtk_text_btree_check found \"%s\" with toggles (%d) but no root",
+ tag->name, info->toggle_count);
+ }
+ continue; /* no ranges for the tag */
+ }
+ else if (info->toggle_count == 0)
+ {
+ g_error("gtk_text_btree_check found root for \"%s\" with no toggles",
+ tag->name);
+ }
+ else if (info->toggle_count & 1)
+ {
+ g_error("gtk_text_btree_check found odd toggle count for \"%s\" (%d)",
+ tag->name, info->toggle_count);
+ }
+ for (summary = node->summary; summary != NULL;
+ summary = summary->next)
+ {
+ if (summary->info->tag == tag)
+ {
+ g_error("gtk_text_btree_check found root GtkTextBTreeNode with summary info");
+ }
+ }
+ count = 0;
+ if (node->level > 0)
+ {
+ for (node = node->children.node ; node != NULL ;
+ node = node->next)
+ {
+ for (summary = node->summary; summary != NULL;
+ summary = summary->next)
+ {
+ if (summary->info->tag == tag)
+ {
+ count += summary->toggle_count;
+ }
+ }
+ }
+ }
+ else
+ {
+ GtkTextLineSegmentClass * last = NULL;
+
+ for (line = node->children.line ; line != NULL ;
+ line = line->next)
+ {
+ for (seg = line->segments; seg != NULL;
+ seg = seg->next)
+ {
+ if ((seg->type == &gtk_text_toggle_on_type ||
+ seg->type == &gtk_text_toggle_off_type) &&
+ seg->body.toggle.info->tag == tag)
+ {
+ if (last == seg->type)
+ g_error ("Two consecutive toggles on or off weren't merged");
+ if (!seg->body.toggle.inNodeCounts)
+ g_error ("Toggle segment not in the node counts");
+
+ last = seg->type;
+
+ count++;
+ }
+ }
+ }
+ }
+ if (count != info->toggle_count)
+ {
+ g_error("gtk_text_btree_check toggle_count (%d) wrong for \"%s\" should be (%d)",
+ info->toggle_count, tag->name, count);
+ }
+ }
+ }
+
+ g_slist_free(taglist);
+ taglist = NULL;
+
+ /*
+ * Call a recursive procedure to do the main body of checks.
+ */
+
+ node = tree->root_node;
+ gtk_text_btree_node_check_consistency(tree->root_node);
+
+ /*
+ * Make sure that there are at least two lines in the text and
+ * that the last line has no characters except a newline.
+ */
+
+ if (node->num_lines < 2)
+ {
+ g_error("gtk_text_btree_check: less than 2 lines in tree");
+ }
+ if (node->num_chars < 2)
+ {
+ g_error("%s: less than 2 chars in tree", __FUNCTION__);
+ }
+ while (node->level > 0)
+ {
+ node = node->children.node;
+ while (node->next != NULL)
+ {
+ node = node->next;
+ }
+ }
+ line = node->children.line;
+ while (line->next != NULL)
+ {
+ line = line->next;
+ }
+ seg = line->segments;
+ while ((seg->type == &gtk_text_toggle_off_type)
+ || (seg->type == &gtk_text_right_mark_type)
+ || (seg->type == &gtk_text_left_mark_type))
+ {
+ /*
+ * It's OK to toggle a tag off in the last line, but
+ * not to start a new range. It's also OK to have marks
+ * in the last line.
+ */
+
+ seg = seg->next;
+ }
+ if (seg->type != &gtk_text_char_type)
+ {
+ g_error("gtk_text_btree_check: last line has bogus segment type");
+ }
+ if (seg->next != NULL)
+ {
+ g_error("gtk_text_btree_check: last line has too many segments");
+ }
+ if (seg->byte_count != 1)
+ {
+ g_error("gtk_text_btree_check: last line has wrong # characters: %d",
+ seg->byte_count);
+ }
+ if ((seg->body.chars[0] != '\n') || (seg->body.chars[1] != 0))
+ {
+ g_error("gtk_text_btree_check: last line had bad value: %s",
+ seg->body.chars);
+ }
+}
+
+void gtk_text_btree_spew_line(GtkTextBTree* tree, GtkTextLine* line);
+void gtk_text_btree_spew_segment(GtkTextBTree* tree, GtkTextLineSegment* seg);
+void gtk_text_btree_spew_node(GtkTextBTreeNode *node, int indent);
+void gtk_text_btree_spew_line_short (GtkTextLine *line, int indent);
+
+void
+gtk_text_btree_spew (GtkTextBTree *tree)
+{
+ GtkTextLine * line;
+ int real_line;
+
+ printf("%d lines in tree %p\n",
+ gtk_text_btree_line_count(tree), tree);
+
+ line = gtk_text_btree_get_line(tree, 0, &real_line);
+
+ while (line != NULL)
+ {
+ gtk_text_btree_spew_line(tree, line);
+ line = gtk_text_line_next(line);
+ }
+
+ printf("=================== Tag information\n");
+
+ {
+ GSList * list;
+
+ list = tree->tag_infos;
+
+ while (list != NULL)
+ {
+ GtkTextTagInfo *info;
+
+ info = list->data;
+
+ printf (" tag `%s': root at %p, toggle count %d\n",
+ info->tag->name, info->tag_root, info->toggle_count);
+
+ list = g_slist_next (list);
+ }
+ }
+
+ printf("=================== Tree nodes\n");
+
+ {
+ gtk_text_btree_spew_node (tree->root_node, 0);
+ }
+}
+
+void
+gtk_text_btree_spew_line_short (GtkTextLine *line, int indent)
+{
+ gchar * spaces;
+ GtkTextLineSegment *seg;
+
+ spaces = g_strnfill (indent, ' ');
+
+ printf ("%sline %p chars %d bytes %d\n",
+ spaces, line,
+ gtk_text_line_char_count (line),
+ gtk_text_line_byte_count (line));
+
+ seg = line->segments;
+ while (seg != NULL)
+ {
+ if (seg->type == &gtk_text_char_type)
+ {
+ gchar* str = g_strndup(seg->body.chars, MIN (seg->byte_count, 10));
+ gchar* s;
+ s = str;
+ while (*s)
+ {
+ if (*s == '\n')
+ *s = '\\';
+ ++s;
+ }
+ printf("%s chars `%s'...\n", spaces, str);
+ g_free(str);
+ }
+ else if (seg->type == &gtk_text_right_mark_type)
+ {
+ printf("%s right mark `%s'\n", spaces, seg->body.mark.name);
+ }
+ else if (seg->type == &gtk_text_left_mark_type)
+ {
+ printf("%s left mark `%s'\n", spaces, seg->body.mark.name);
+ }
+ else if (seg->type == &gtk_text_toggle_on_type ||
+ seg->type == &gtk_text_toggle_off_type)
+ {
+ printf("%s tag `%s' %s\n",
+ spaces, seg->body.toggle.info->tag->name,
+ seg->type == &gtk_text_toggle_off_type ? "off" : "on");
+ }
+
+ seg = seg->next;
+ }
+
+ g_free (spaces);
+}
+
+void
+gtk_text_btree_spew_node(GtkTextBTreeNode *node, int indent)
+{
+ gchar * spaces;
+ GtkTextBTreeNode *iter;
+ Summary *s;
+
+ spaces = g_strnfill (indent, ' ');
+
+ printf ("%snode %p level %d children %d lines %d chars %d\n",
+ spaces, node, node->level,
+ node->num_children, node->num_lines, node->num_chars);
+
+ s = node->summary;
+ while (s)
+ {
+ printf ("%s %d toggles of `%s' below this node\n",
+ spaces, s->toggle_count, s->info->tag->name);
+ s = s->next;
+ }
+
+ g_free (spaces);
+
+ if (node->level > 0)
+ {
+ iter = node->children.node;
+ while (iter != NULL)
+ {
+ gtk_text_btree_spew_node (iter, indent + 2);
+
+ iter = iter->next;
+ }
+ }
+ else
+ {
+ GtkTextLine *line = node->children.line;
+ while (line != NULL)
+ {
+ gtk_text_btree_spew_line_short (line, indent + 2);
+
+ line = line->next;
+ }
+ }
+}
+
+void
+gtk_text_btree_spew_line(GtkTextBTree* tree, GtkTextLine* line)
+{
+ GtkTextLineSegment * seg;
+
+ printf("%4d| line: %p parent: %p next: %p\n",
+ gtk_text_line_get_number(line), line, line->parent, line->next);
+
+ seg = line->segments;
+
+ while (seg != NULL)
+ {
+ gtk_text_btree_spew_segment(tree, seg);
+ seg = seg->next;
+ }
+}
+
+void
+gtk_text_btree_spew_segment(GtkTextBTree* tree, GtkTextLineSegment * seg)
+{
+ printf(" segment: %p type: %s bytes: %d chars: %d\n",
+ seg, seg->type->name, seg->byte_count, seg->char_count);
+
+ if (seg->type == &gtk_text_char_type)
+ {
+ gchar* str = g_strndup(seg->body.chars, seg->byte_count);
+ printf(" `%s'\n", str);
+ g_free(str);
+ }
+ else if (seg->type == &gtk_text_right_mark_type)
+ {
+ printf(" right mark `%s'\n", seg->body.mark.name);
+ }
+ else if (seg->type == &gtk_text_left_mark_type)
+ {
+ printf(" left mark `%s'\n", seg->body.mark.name);
+ }
+ else if (seg->type == &gtk_text_toggle_on_type ||
+ seg->type == &gtk_text_toggle_off_type)
+ {
+ printf(" tag `%s' priority %d\n",
+ seg->body.toggle.info->tag->name,
+ seg->body.toggle.info->tag->priority);
+ }
+}
+
+
diff --git a/gtk/gtktextbtree.h b/gtk/gtktextbtree.h
new file mode 100644
index 000000000..91b37af75
--- /dev/null
+++ b/gtk/gtktextbtree.h
@@ -0,0 +1,279 @@
+#ifndef GTK_TEXT_BTREE_H
+#define GTK_TEXT_BTREE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <gtk/gtktextbuffer.h>
+#include <gtk/gtktexttag.h>
+#include <gtk/gtktextmark.h>
+#include <gtk/gtktextchild.h>
+#include <gtk/gtktextsegment.h>
+#include <gtk/gtktextiter.h>
+
+
+GtkTextBTree *gtk_text_btree_new (GtkTextTagTable *table,
+ GtkTextBuffer *buffer);
+void gtk_text_btree_ref (GtkTextBTree *tree);
+void gtk_text_btree_unref (GtkTextBTree *tree);
+GtkTextBuffer *gtk_text_btree_get_buffer (GtkTextBTree *tree);
+
+
+guint gtk_text_btree_get_chars_changed_stamp (GtkTextBTree *tree);
+guint gtk_text_btree_get_segments_changed_stamp (GtkTextBTree *tree);
+void gtk_text_btree_segments_changed (GtkTextBTree *tree);
+
+
+/* Indexable segment mutation */
+
+void gtk_text_btree_delete (GtkTextIter *start,
+ GtkTextIter *end);
+void gtk_text_btree_insert (GtkTextIter *iter,
+ const gchar *text,
+ gint len);
+void gtk_text_btree_insert_pixmap (GtkTextIter *iter,
+ GdkPixmap *pixmap,
+ GdkBitmap *mask);
+
+
+
+/* View stuff */
+GtkTextLine *gtk_text_btree_find_line_by_y (GtkTextBTree *tree,
+ gpointer view_id,
+ gint ypixel,
+ gint *line_top_y);
+gint gtk_text_btree_find_line_top (GtkTextBTree *tree,
+ GtkTextLine *line,
+ gpointer view_id);
+void gtk_text_btree_add_view (GtkTextBTree *tree,
+ GtkTextLayout *layout);
+void gtk_text_btree_remove_view (GtkTextBTree *tree,
+ gpointer view_id);
+void gtk_text_btree_invalidate_region (GtkTextBTree *tree,
+ const GtkTextIter *start,
+ const GtkTextIter *end);
+void gtk_text_btree_get_view_size (GtkTextBTree *tree,
+ gpointer view_id,
+ gint *width,
+ gint *height);
+gboolean gtk_text_btree_is_valid (GtkTextBTree *tree,
+ gpointer view_id);
+gboolean gtk_text_btree_validate (GtkTextBTree *tree,
+ gpointer view_id,
+ gint max_pixels,
+ gint *y,
+ gint *old_height,
+ gint *new_height);
+void gtk_text_btree_validate_line (GtkTextBTree *tree,
+ GtkTextLine *line,
+ gpointer view_id);
+
+/* Tag */
+
+void gtk_text_btree_tag (const GtkTextIter *start,
+ const GtkTextIter *end,
+ GtkTextTag *tag,
+ gboolean apply);
+
+/* "Getters" */
+
+GtkTextLine * gtk_text_btree_get_line (GtkTextBTree *tree,
+ gint line_number,
+ gint *real_line_number);
+GtkTextLine * gtk_text_btree_get_line_at_char (GtkTextBTree *tree,
+ gint char_index,
+ gint *line_start_index,
+ gint *real_char_index);
+GtkTextTag** gtk_text_btree_get_tags (const GtkTextIter *iter,
+ gint *num_tags);
+gchar *gtk_text_btree_get_text (const GtkTextIter *start,
+ const GtkTextIter *end,
+ gboolean include_hidden,
+ gboolean include_nonchars);
+gint gtk_text_btree_line_count (GtkTextBTree *tree);
+gint gtk_text_btree_char_count (GtkTextBTree *tree);
+gboolean gtk_text_btree_char_is_invisible (const GtkTextIter *iter);
+
+
+
+/* Get iterators (these are implemented in gtktextiter.c) */
+void gtk_text_btree_get_iter_at_char (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ gint char_index);
+void gtk_text_btree_get_iter_at_line_char (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ gint line_number,
+ gint char_index);
+void gtk_text_btree_get_iter_at_line_byte (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ gint line_number,
+ gint byte_index);
+gboolean gtk_text_btree_get_iter_from_string (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ const gchar *string);
+gboolean gtk_text_btree_get_iter_at_mark_name (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ const gchar *mark_name);
+void gtk_text_btree_get_iter_at_mark (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ GtkTextLineSegment *mark);
+void gtk_text_btree_get_last_iter (GtkTextBTree *tree,
+ GtkTextIter *iter);
+void gtk_text_btree_get_iter_at_line (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ GtkTextLine *line,
+ gint byte_offset);
+gboolean gtk_text_btree_get_iter_at_first_toggle (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ GtkTextTag *tag);
+gboolean gtk_text_btree_get_iter_at_last_toggle (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ GtkTextTag *tag);
+
+
+/* Manipulate marks */
+GtkTextLineSegment *gtk_text_btree_set_mark (GtkTextBTree *tree,
+ const gchar *name,
+ gboolean left_gravity,
+ const GtkTextIter *index,
+ gboolean should_exist);
+void gtk_text_btree_remove_mark_by_name (GtkTextBTree *tree,
+ const gchar *name);
+void gtk_text_btree_remove_mark (GtkTextBTree *tree,
+ GtkTextLineSegment *segment);
+gboolean gtk_text_btree_get_selection_bounds (GtkTextBTree *tree,
+ GtkTextIter *start,
+ GtkTextIter *end);
+void gtk_text_btree_place_cursor (GtkTextBTree *tree,
+ const GtkTextIter *where);
+gboolean gtk_text_btree_mark_is_insert (GtkTextBTree *tree,
+ GtkTextLineSegment *segment);
+gboolean gtk_text_btree_mark_is_selection_bound (GtkTextBTree *tree,
+ GtkTextLineSegment *segment);
+GtkTextLineSegment *gtk_text_btree_get_mark_by_name (GtkTextBTree *tree,
+ const gchar *name);
+GtkTextLine * gtk_text_btree_first_could_contain_tag (GtkTextBTree *tree,
+ GtkTextTag *tag);
+GtkTextLine * gtk_text_btree_last_could_contain_tag (GtkTextBTree *tree,
+ GtkTextTag *tag);
+
+/* Lines */
+
+/* Chunk of data associated with a line; views can use this to store
+ info at the line. They should "subclass" the header struct here. */
+typedef struct _GtkTextLineData GtkTextLineData;
+
+struct _GtkTextLineData {
+ gpointer view_id;
+ GtkTextLineData *next;
+ gint height;
+ gint width : 24;
+ gint valid : 8;
+};
+
+/*
+ * The data structure below defines a single line of text (from newline
+ * to newline, not necessarily what appears on one line of the screen).
+ *
+ * You can consider this line a "paragraph" also
+ */
+
+struct _GtkTextLine {
+ GtkTextBTreeNode *parent; /* Pointer to parent node containing
+ * line. */
+ GtkTextLine *next; /* Next in linked list of lines with
+ * same parent node in B-tree. NULL
+ * means end of list. */
+ GtkTextLineSegment *segments; /* First in ordered list of segments
+ * that make up the line. */
+ GtkTextLineData *views; /* data stored here by views */
+};
+
+
+gint gtk_text_line_get_number (GtkTextLine *line);
+gboolean gtk_text_line_char_has_tag (GtkTextLine *line,
+ GtkTextBTree *tree,
+ gint char_in_line,
+ GtkTextTag *tag);
+gboolean gtk_text_line_byte_has_tag (GtkTextLine *line,
+ GtkTextBTree *tree,
+ gint byte_in_line,
+ GtkTextTag *tag);
+GtkTextLine * gtk_text_line_next (GtkTextLine *line);
+GtkTextLine * gtk_text_line_previous (GtkTextLine *line);
+void gtk_text_line_add_data (GtkTextLine *line,
+ GtkTextLineData *data);
+gpointer gtk_text_line_remove_data (GtkTextLine *line,
+ gpointer view_id);
+gpointer gtk_text_line_get_data (GtkTextLine *line,
+ gpointer view_id);
+void gtk_text_line_invalidate_wrap (GtkTextLine *line,
+ GtkTextLineData *ld);
+gint gtk_text_line_char_count (GtkTextLine *line);
+gint gtk_text_line_byte_count (GtkTextLine *line);
+gint gtk_text_line_char_index (GtkTextLine *line);
+GtkTextLineSegment *gtk_text_line_byte_to_segment (GtkTextLine *line,
+ gint byte_offset,
+ gint *seg_offset);
+GtkTextLineSegment *gtk_text_line_char_to_segment (GtkTextLine *line,
+ gint char_offset,
+ gint *seg_offset);
+void gtk_text_line_byte_locate (GtkTextLine *line,
+ gint byte_offset,
+ GtkTextLineSegment **segment,
+ GtkTextLineSegment **any_segment,
+ gint *seg_byte_offset,
+ gint *line_byte_offset);
+void gtk_text_line_char_locate (GtkTextLine *line,
+ gint char_offset,
+ GtkTextLineSegment **segment,
+ GtkTextLineSegment **any_segment,
+ gint *seg_char_offset,
+ gint *line_char_offset);
+void gtk_text_line_byte_to_char_offsets (GtkTextLine *line,
+ gint byte_offset,
+ gint *line_char_offset,
+ gint *seg_char_offset);
+void gtk_text_line_char_to_byte_offsets (GtkTextLine *line,
+ gint char_offset,
+ gint *line_byte_offset,
+ gint *seg_byte_offset);
+GtkTextLineSegment *gtk_text_line_byte_to_any_segment (GtkTextLine *line,
+ gint byte_offset,
+ gint *seg_offset);
+GtkTextLineSegment *gtk_text_line_char_to_any_segment (GtkTextLine *line,
+ gint char_offset,
+ gint *seg_offset);
+gint gtk_text_line_byte_to_char (GtkTextLine *line,
+ gint byte_offset);
+gint gtk_text_line_char_to_byte (GtkTextLine *line,
+ gint char_offset);
+GtkTextLine * gtk_text_line_next_could_contain_tag (GtkTextLine *line,
+ GtkTextBTree *tree,
+ GtkTextTag *tag);
+GtkTextLine * gtk_text_line_previous_could_contain_tag (GtkTextLine *line,
+ GtkTextBTree *tree,
+ GtkTextTag *tag);
+
+
+/* Debug */
+void gtk_text_btree_check (GtkTextBTree *tree);
+void gtk_text_btree_spew (GtkTextBTree *tree);
+extern gboolean gtk_text_view_debug_btree;
+
+/* ignore, exported only for gtktextsegment.c */
+void toggle_segment_check_func (GtkTextLineSegment *segPtr,
+ GtkTextLine *line);
+void change_node_toggle_count (GtkTextBTreeNode *node,
+ GtkTextTagInfo *info,
+ gint delta);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
+
diff --git a/gtk/gtktextbuffer.c b/gtk/gtktextbuffer.c
new file mode 100644
index 000000000..8fe51fb13
--- /dev/null
+++ b/gtk/gtktextbuffer.c
@@ -0,0 +1,1652 @@
+/* gtktextbuffer.c - the "model" in the MVC text widget architecture
+ * Copyright (c) 2000 Red Hat, Inc.
+ * Developed by Havoc Pennington
+ */
+
+#include "gtkinvisible.h"
+#include "gtkselection.h"
+#include "gtksignal.h"
+#include "gtktextbuffer.h"
+#include "gtktextbtree.h"
+#include "gtktextiterprivate.h"
+
+enum {
+ INSERT_TEXT,
+ DELETE_TEXT,
+ CHANGED,
+ MODIFIED_CHANGED,
+ MARK_SET,
+ MARK_DELETED,
+ APPLY_TAG,
+ REMOVE_TAG,
+ LAST_SIGNAL
+};
+
+enum {
+ ARG_0,
+ LAST_ARG
+};
+
+enum {
+ TARGET_STRING,
+ TARGET_TEXT,
+ TARGET_COMPOUND_TEXT,
+ TARGET_UTF8_STRING
+};
+
+static void gtk_text_buffer_init (GtkTextBuffer *tkxt_buffer);
+static void gtk_text_buffer_class_init (GtkTextBufferClass *klass);
+static void gtk_text_buffer_destroy (GtkObject *object);
+static void gtk_text_buffer_finalize (GObject *object);
+
+
+static void gtk_text_buffer_update_primary_selection (GtkTextBuffer *buffer);
+static void gtk_text_buffer_update_clipboard_selection (GtkTextBuffer *buffer);
+static void gtk_text_buffer_real_insert_text (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ const gchar *text,
+ gint len);
+static void gtk_text_buffer_real_delete_text (GtkTextBuffer *buffer,
+ GtkTextIter *start,
+ GtkTextIter *end);
+static void gtk_text_buffer_real_apply_tag (GtkTextBuffer *buffer,
+ GtkTextTag *tag,
+ const GtkTextIter *start_char,
+ const GtkTextIter *end_char);
+static void gtk_text_buffer_real_remove_tag (GtkTextBuffer *buffer,
+ GtkTextTag *tag,
+ const GtkTextIter *start_char,
+ const GtkTextIter *end_char);
+
+
+void gtk_marshal_NONE__INT_POINTER_INT (GtkObject *object,
+ GtkSignalFunc func,
+ gpointer func_data,
+ GtkArg *args);
+
+static GdkAtom clipboard_atom = GDK_NONE;
+static GdkAtom text_atom = GDK_NONE;
+static GdkAtom ctext_atom = GDK_NONE;
+static GdkAtom utf8_atom = GDK_NONE;
+
+static GtkObjectClass *parent_class = NULL;
+static guint signals[LAST_SIGNAL] = { 0 };
+
+GtkType
+gtk_text_buffer_get_type (void)
+{
+ static GtkType our_type = 0;
+
+ if (our_type == 0)
+ {
+ static const GtkTypeInfo our_info =
+ {
+ "GtkTextBuffer",
+ sizeof (GtkTextBuffer),
+ sizeof (GtkTextBufferClass),
+ (GtkClassInitFunc) gtk_text_buffer_class_init,
+ (GtkObjectInitFunc) gtk_text_buffer_init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL
+ };
+
+ our_type = gtk_type_unique (GTK_TYPE_OBJECT, &our_info);
+ }
+
+ return our_type;
+}
+
+static void
+gtk_text_buffer_class_init (GtkTextBufferClass *klass)
+{
+ GtkObjectClass *object_class = (GtkObjectClass*) klass;
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ parent_class = gtk_type_class (GTK_TYPE_OBJECT);
+
+ signals[INSERT_TEXT] =
+ gtk_signal_new ("insert_text",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextBufferClass, insert_text),
+ gtk_marshal_NONE__INT_POINTER_INT,
+ GTK_TYPE_NONE,
+ 3,
+ GTK_TYPE_INT,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_INT);
+
+ signals[DELETE_TEXT] =
+ gtk_signal_new ("delete_text",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextBufferClass, delete_text),
+ gtk_marshal_NONE__INT_INT,
+ GTK_TYPE_NONE,
+ 2,
+ GTK_TYPE_INT,
+ GTK_TYPE_INT);
+
+ signals[CHANGED] =
+ gtk_signal_new ("changed",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextBufferClass, changed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE,
+ 0);
+
+ signals[MODIFIED_CHANGED] =
+ gtk_signal_new ("modified_changed",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextBufferClass, modified_changed),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE,
+ 0);
+
+ signals[MARK_SET] =
+ gtk_signal_new ("mark_set",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextBufferClass, mark_set),
+ gtk_marshal_NONE__POINTER_POINTER,
+ GTK_TYPE_NONE,
+ 2,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_POINTER);
+
+ signals[MARK_DELETED] =
+ gtk_signal_new ("mark_deleted",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextBufferClass, mark_deleted),
+ gtk_marshal_NONE__POINTER,
+ GTK_TYPE_NONE,
+ 1,
+ GTK_TYPE_POINTER);
+
+ signals[APPLY_TAG] =
+ gtk_signal_new ("apply_tag",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextBufferClass, apply_tag),
+ gtk_marshal_NONE__POINTER_INT_INT,
+ GTK_TYPE_NONE,
+ 3,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_INT,
+ GTK_TYPE_INT);
+
+ signals[REMOVE_TAG] =
+ gtk_signal_new ("remove_tag",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextBufferClass, remove_tag),
+ gtk_marshal_NONE__POINTER_INT_INT,
+ GTK_TYPE_NONE,
+ 3,
+ GTK_TYPE_POINTER,
+ GTK_TYPE_INT,
+ GTK_TYPE_INT);
+
+ gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+
+ object_class->destroy = gtk_text_buffer_destroy;
+
+ gobject_class->finalize = gtk_text_buffer_finalize;
+
+ klass->insert_text = gtk_text_buffer_real_insert_text;
+ klass->delete_text = gtk_text_buffer_real_delete_text;
+ klass->apply_tag = gtk_text_buffer_real_apply_tag;
+ klass->remove_tag = gtk_text_buffer_real_remove_tag;
+}
+
+
+typedef gint (*GtkSignal_NONE__INT_POINTER_INT) (GtkObject *object,
+ gint pos,
+ const gchar *text,
+ gint len,
+ gpointer user_data);
+
+void
+gtk_marshal_NONE__INT_POINTER_INT (GtkObject *object,
+ GtkSignalFunc func,
+ gpointer func_data,
+ GtkArg *args)
+{
+ GtkSignal_NONE__INT_POINTER_INT rfunc;
+
+ rfunc = (GtkSignal_NONE__INT_POINTER_INT) func;
+
+ (*rfunc) (object,
+ GTK_VALUE_INT (args[0]),
+ GTK_VALUE_POINTER (args[1]),
+ GTK_VALUE_INT (args[2]),
+ func_data);
+}
+
+void
+gtk_text_buffer_init (GtkTextBuffer *buffer)
+{
+ static const GtkTargetEntry targets[] = {
+ { "STRING", 0, TARGET_STRING },
+ { "TEXT", 0, TARGET_TEXT },
+ { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
+ { "UTF8_STRING", 0, TARGET_UTF8_STRING }
+ };
+ static const gint n_targets = sizeof(targets) / sizeof(targets[0]);
+
+ if (!clipboard_atom)
+ clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE);
+
+ if (!text_atom)
+ text_atom = gdk_atom_intern ("TEXT", FALSE);
+
+ if (!ctext_atom)
+ ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
+
+ if (!utf8_atom)
+ utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
+
+ buffer->selection_widget = gtk_invisible_new();
+
+ gtk_selection_add_targets (buffer->selection_widget,
+ GDK_SELECTION_PRIMARY,
+ targets, n_targets);
+ gtk_selection_add_targets (buffer->selection_widget,
+ clipboard_atom,
+ targets, n_targets);
+}
+
+GtkTextBuffer*
+gtk_text_buffer_new (GtkTextTagTable *table)
+{
+ GtkTextBuffer *text_buffer;
+
+ /* This is broken, need construct_only argument for the tag table
+ so language bindings can set it.
+ */
+
+ text_buffer = GTK_TEXT_BUFFER (gtk_type_new (gtk_text_buffer_get_type ()));
+
+ if (table)
+ text_buffer->tag_table = table;
+ else
+ text_buffer->tag_table = gtk_text_tag_table_new();
+
+ gtk_object_ref (GTK_OBJECT(text_buffer->tag_table));
+ gtk_object_sink (GTK_OBJECT(text_buffer->tag_table));
+
+ text_buffer->tree = gtk_text_btree_new(text_buffer->tag_table,
+ text_buffer);
+
+ return text_buffer;
+}
+
+static void
+gtk_text_buffer_destroy (GtkObject *object)
+{
+ GtkTextBuffer *buffer;
+
+ buffer = GTK_TEXT_BUFFER (object);
+
+ gtk_widget_destroy(buffer->selection_widget);
+ buffer->selection_widget = NULL;
+
+ gtk_object_unref(GTK_OBJECT(buffer->tag_table));
+ buffer->tag_table = NULL;
+
+ gtk_text_btree_unref(buffer->tree);
+ buffer->tree = NULL;
+
+ (* parent_class->destroy) (object);
+}
+
+static void
+gtk_text_buffer_finalize (GObject *object)
+{
+ GtkTextBuffer *tkxt_buffer;
+
+ tkxt_buffer = GTK_TEXT_BUFFER (object);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+/*
+ * Insertion
+ */
+
+static void
+gtk_text_buffer_real_insert_text(GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ const gchar *text,
+ gint len)
+{
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+ g_return_if_fail(iter != NULL);
+
+ gtk_text_btree_insert(iter, text, len);
+
+ gtk_signal_emit(GTK_OBJECT(buffer), signals[CHANGED]);
+
+ gtk_text_buffer_set_modified(buffer, TRUE);
+}
+
+static void
+gtk_text_buffer_emit_insert(GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ const gchar *text,
+ gint len)
+{
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+ g_return_if_fail(iter != NULL);
+ g_return_if_fail(text != NULL);
+
+ if (len < 0)
+ len = strlen(text);
+
+ if (len > 0)
+ {
+ gtk_signal_emit(GTK_OBJECT(buffer), signals[INSERT_TEXT],
+ iter, text, len);
+ }
+}
+
+void
+gtk_text_buffer_insert (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ const gchar *text,
+ gint len)
+{
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+ g_return_if_fail(iter != NULL);
+ g_return_if_fail(text != NULL);
+
+ gtk_text_buffer_emit_insert(buffer, iter, text, len);
+}
+
+void
+gtk_text_buffer_insert_at_cursor (GtkTextBuffer *buffer,
+ const gchar *text,
+ gint len)
+{
+ GtkTextIter iter;
+
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+ g_return_if_fail(text != NULL);
+
+ gtk_text_buffer_get_iter_at_mark(buffer, &iter, "insert");
+
+ gtk_text_buffer_insert(buffer, &iter, text, len);
+}
+
+void
+gtk_text_buffer_insert_at_char (GtkTextBuffer *buffer,
+ gint char_pos,
+ const gchar *text,
+ gint len)
+{
+ GtkTextIter iter;
+
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+ g_return_if_fail(text != NULL);
+
+ gtk_text_buffer_get_iter_at_char(buffer, &iter, char_pos);
+
+ gtk_text_buffer_insert(buffer, &iter, text, len);
+}
+
+void
+gtk_text_buffer_insert_after_line(GtkTextBuffer *buffer,
+ gint after_this_line,
+ const gchar *text,
+ gint len)
+{
+ GtkTextIter line;
+
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+ g_return_if_fail(text != NULL);
+
+ gtk_text_buffer_get_iter_at_line(buffer,
+ &line,
+ after_this_line);
+
+ /* Start of the next line */
+ gtk_text_iter_forward_line(&line);
+
+ gtk_text_buffer_insert(buffer, &line, text, len);
+}
+
+/*
+ * Deletion
+ */
+
+static void
+gtk_text_buffer_real_delete_text(GtkTextBuffer *buffer,
+ GtkTextIter *start,
+ GtkTextIter *end)
+{
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+ g_return_if_fail(start != NULL);
+ g_return_if_fail(end != NULL);
+
+ gtk_text_btree_delete(start, end);
+
+ /* may have deleted the selection... */
+ gtk_text_buffer_update_primary_selection(buffer);
+
+ gtk_signal_emit(GTK_OBJECT(buffer), signals[CHANGED]);
+
+ gtk_text_buffer_set_modified(buffer, TRUE);
+}
+
+static void
+gtk_text_buffer_emit_delete(GtkTextBuffer *buffer,
+ GtkTextIter *start,
+ GtkTextIter *end)
+{
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+ g_return_if_fail(start != NULL);
+ g_return_if_fail(end != NULL);
+
+ if (gtk_text_iter_equal(start, end))
+ return;
+
+ gtk_signal_emit(GTK_OBJECT(buffer),
+ signals[DELETE_TEXT],
+ start, end);
+}
+
+void
+gtk_text_buffer_delete (GtkTextBuffer *buffer,
+ GtkTextIter *start,
+ GtkTextIter *end)
+{
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+ g_return_if_fail(start != NULL);
+ g_return_if_fail(end != NULL);
+
+ gtk_text_buffer_emit_delete(buffer, start, end);
+}
+
+void
+gtk_text_buffer_delete_chars (GtkTextBuffer *buffer,
+ gint start_char,
+ gint end_char)
+{
+ GtkTextIter start;
+ GtkTextIter end;
+
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+
+ if (start_char == end_char)
+ return;
+
+ gtk_text_buffer_get_iter_at_char(buffer, &start, start_char);
+ gtk_text_buffer_get_iter_at_char(buffer, &end, end_char);
+
+ gtk_text_buffer_emit_delete(buffer, &start, &end);
+}
+
+void
+gtk_text_buffer_delete_lines(GtkTextBuffer *buffer,
+ gint start_line,
+ gint end_line)
+{
+ GtkTextIter start;
+ GtkTextIter end;
+
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+
+ if (start_line == end_line)
+ return;
+
+ /* start of the start line */
+ gtk_text_buffer_get_iter_at_line(buffer, &start, start_line);
+
+ /* start of the end line; note that we don't delete the end_line, we
+ want to delete up to the start of it */
+ gtk_text_buffer_get_iter_at_line(buffer, &end, end_line);
+
+ gtk_text_buffer_delete (buffer, &start, &end);
+}
+
+void
+gtk_text_buffer_delete_from_line(GtkTextBuffer *buffer,
+ gint line,
+ gint start_char, gint end_char)
+{
+ GtkTextIter start;
+ GtkTextIter end;
+
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+
+ if (start_char == end_char)
+ return;
+
+ gtk_text_buffer_get_iter_at_line_char(buffer, &start, line, start_char);
+ gtk_text_buffer_get_iter_at_line_char(buffer, &end, line, end_char);
+
+ gtk_text_buffer_delete(buffer, &start, &end);
+}
+
+/*
+ * Extracting textual buffer contents
+ */
+
+gchar*
+gtk_text_buffer_get_text (GtkTextBuffer *buffer,
+ const GtkTextIter *start,
+ const GtkTextIter *end,
+ gboolean include_hidden_chars)
+{
+ g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
+ g_return_val_if_fail(start != NULL, NULL);
+ g_return_val_if_fail(end != NULL, NULL);
+
+ if (include_hidden_chars)
+ return gtk_text_iter_get_text(start, end);
+ else
+ return gtk_text_iter_get_visible_text(start, end);
+}
+
+gchar*
+gtk_text_buffer_get_text_chars (GtkTextBuffer *buffer,
+ gint start_char,
+ gint end_char,
+ gboolean include_hidden_chars)
+{
+ GtkTextIter start;
+ GtkTextIter end;
+
+ g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
+
+ if (start_char == end_char)
+ return g_strdup("");
+
+ gtk_text_buffer_get_iter_at_char (buffer, &start, start_char);
+ gtk_text_buffer_get_iter_at_char (buffer, &end, end_char);
+
+ return gtk_text_buffer_get_text(buffer, &start, &end,
+ include_hidden_chars);
+}
+
+gchar*
+gtk_text_buffer_get_text_from_line (GtkTextBuffer *buffer,
+ gint line,
+ gint start_char,
+ gint end_char,
+ gboolean include_hidden_chars)
+{
+ GtkTextIter start;
+ GtkTextIter end;
+
+ g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
+
+ if (start_char == end_char)
+ return g_strdup("");
+
+ gtk_text_buffer_get_iter_at_line_char (buffer, &start, line, start_char);
+ gtk_text_buffer_get_iter_at_line_char (buffer, &end, line, end_char);
+
+ return gtk_text_buffer_get_text(buffer, &start, &end,
+ include_hidden_chars);
+}
+
+gchar*
+gtk_text_buffer_get_slice (GtkTextBuffer *buffer,
+ const GtkTextIter *start,
+ const GtkTextIter *end,
+ gboolean include_hidden_chars)
+{
+ g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
+ g_return_val_if_fail(start != NULL, NULL);
+ g_return_val_if_fail(end != NULL, NULL);
+
+ if (include_hidden_chars)
+ return gtk_text_iter_get_slice(start, end);
+ else
+ return gtk_text_iter_get_visible_slice(start, end);
+}
+
+gchar*
+gtk_text_buffer_get_slice_chars (GtkTextBuffer *buffer,
+ gint start_char,
+ gint end_char,
+ gboolean include_hidden_chars)
+{
+ GtkTextIter start;
+ GtkTextIter end;
+
+ g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
+
+ if (start_char == end_char)
+ return g_strdup("");
+
+ gtk_text_buffer_get_iter_at_char (buffer, &start, start_char);
+ gtk_text_buffer_get_iter_at_char (buffer, &end, end_char);
+
+ return gtk_text_buffer_get_slice(buffer, &start, &end,
+ include_hidden_chars);
+}
+
+gchar*
+gtk_text_buffer_get_slice_from_line (GtkTextBuffer *buffer,
+ gint line,
+ gint start_char,
+ gint end_char,
+ gboolean include_hidden_chars)
+{
+ GtkTextIter start;
+ GtkTextIter end;
+
+ g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
+
+ if (start_char == end_char)
+ return g_strdup("");
+
+ gtk_text_buffer_get_iter_at_line_char (buffer, &start, line, start_char);
+ gtk_text_buffer_get_iter_at_line_char (buffer, &end, line, end_char);
+
+ return gtk_text_buffer_get_slice(buffer, &start, &end,
+ include_hidden_chars);
+}
+
+/*
+ * Pixmaps
+ */
+
+void
+gtk_text_buffer_insert_pixmap (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ GdkPixmap *pixmap,
+ GdkBitmap *mask)
+{
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+ g_return_if_fail(iter != NULL);
+ g_return_if_fail(pixmap != NULL);
+
+ gtk_text_btree_insert_pixmap(iter, pixmap, mask);
+
+ /* FIXME pixmap-specific signal like insert_text */
+
+ gtk_signal_emit(GTK_OBJECT(buffer), signals[CHANGED]);
+
+ gtk_text_buffer_set_modified(buffer, TRUE);
+}
+
+void
+gtk_text_buffer_insert_pixmap_at_char (GtkTextBuffer *buffer,
+ gint char_pos,
+ GdkPixmap *pixmap,
+ GdkBitmap *mask)
+{
+ GtkTextIter iter;
+
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+ g_return_if_fail(pixmap != NULL);
+
+ gtk_text_buffer_get_iter_at_char(buffer, &iter, char_pos);
+
+ gtk_text_buffer_insert_pixmap(buffer, &iter, pixmap, mask);
+}
+
+/*
+ * Mark manipulation
+ */
+
+static void
+gtk_text_buffer_mark_set (GtkTextBuffer *buffer,
+ const GtkTextIter *location,
+ const char *mark_name)
+{
+ /* IMO this should NOT work like insert_text and delete_text,
+ where the real action happens in the default handler.
+
+ The reason is that the default handler would be _required_,
+ i.e. the whole widget would start breaking and segfaulting
+ if the default handler didn't get run. So you can't really
+ override the default handler or stop the emission; that is,
+ this signal is purely for notification, and not to allow users
+ to modify the default behavior. */
+ gtk_signal_emit(GTK_OBJECT(buffer),
+ signals[MARK_SET],
+ &location,
+ mark_name);
+
+}
+
+/**
+ * gtk_text_buffer_set_mark:
+ * @buffer: a #GtkTextBuffer
+ * @mark_name: name of the mark
+ * @iter: location for the mark.
+ * @left_gravity: if the mark is created by this function, gravity for the new
+ * mark.
+ * @should_exist: if %TRUE, warn if the mark does not exist, and return
+ * immediately.
+ *
+ * Move the mark to the given position, if not @should_exist, create the mark.
+ *
+ * Return value:
+ **/
+static GtkTextMark*
+gtk_text_buffer_set_mark(GtkTextBuffer *buffer,
+ const gchar *mark_name,
+ const GtkTextIter *iter,
+ gboolean left_gravity,
+ gboolean should_exist)
+{
+ GtkTextIter location;
+ GtkTextLineSegment *mark;
+
+ g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
+
+ mark = gtk_text_btree_set_mark(buffer->tree,
+ mark_name,
+ left_gravity,
+ iter,
+ should_exist);
+
+ if (gtk_text_btree_mark_is_insert(buffer->tree, mark) ||
+ gtk_text_btree_mark_is_selection_bound(buffer->tree, mark))
+ {
+ gtk_text_buffer_update_primary_selection(buffer);
+ }
+
+ gtk_text_btree_get_iter_at_mark(buffer->tree,
+ &location,
+ mark);
+
+ gtk_text_buffer_mark_set (buffer, &location, mark_name);
+
+ return (GtkTextMark*)mark;
+}
+
+GtkTextMark*
+gtk_text_buffer_create_mark(GtkTextBuffer *buffer,
+ const gchar *mark_name,
+ const GtkTextIter *where,
+ gboolean left_gravity)
+{
+ return gtk_text_buffer_set_mark(buffer, mark_name, where,
+ left_gravity, FALSE);
+}
+
+void
+gtk_text_buffer_move_mark(GtkTextBuffer *buffer,
+ const gchar *mark_name,
+ const GtkTextIter *where)
+{
+ gtk_text_buffer_set_mark(buffer, mark_name, where, FALSE, TRUE);
+}
+
+gboolean
+gtk_text_buffer_get_iter_at_mark(GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ const gchar *name)
+{
+ g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), FALSE);
+
+ return gtk_text_btree_get_iter_at_mark_name(buffer->tree,
+ iter,
+ name);
+}
+
+void
+gtk_text_buffer_delete_mark(GtkTextBuffer *buffer,
+ const gchar *name)
+{
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+
+ if (strcmp(name, "insert") == 0 ||
+ strcmp(name, "selection_bound") == 0)
+ {
+ g_warning("Can't delete special mark `%s'", name);
+ return;
+ }
+
+ gtk_text_btree_remove_mark_by_name(buffer->tree, name);
+
+ /* See rationale above for MARK_SET on why we emit this after
+ removing the mark, rather than removing the mark in a default
+ handler. */
+ gtk_signal_emit(GTK_OBJECT(buffer), signals[MARK_DELETED],
+ name);
+}
+
+GtkTextMark*
+gtk_text_buffer_get_mark (GtkTextBuffer *buffer,
+ const gchar *name)
+{
+ GtkTextLineSegment *seg;
+
+ g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ seg = gtk_text_btree_get_mark_by_name(buffer->tree, name);
+
+ return (GtkTextMark*)seg;
+}
+
+void
+gtk_text_buffer_place_cursor (GtkTextBuffer *buffer,
+ const GtkTextIter *where)
+{
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+
+ gtk_text_btree_place_cursor(buffer->tree, where);
+ gtk_text_buffer_mark_set (buffer, where, "insert");
+ gtk_text_buffer_mark_set (buffer, where, "selection_bound");
+}
+
+/*
+ * Tags
+ */
+
+GtkTextTag*
+gtk_text_buffer_create_tag(GtkTextBuffer *buffer,
+ const gchar *tag_name)
+{
+ GtkTextTag *tag;
+
+ g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), NULL);
+ g_return_val_if_fail(tag_name != NULL, NULL);
+
+ tag = gtk_text_tag_new(tag_name);
+
+ gtk_text_tag_table_add(buffer->tag_table, tag);
+
+ return tag;
+}
+
+static void
+gtk_text_buffer_real_apply_tag (GtkTextBuffer *buffer,
+ GtkTextTag *tag,
+ const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ gtk_text_btree_tag(start, end, tag, TRUE);
+}
+
+static void
+gtk_text_buffer_real_remove_tag (GtkTextBuffer *buffer,
+ GtkTextTag *tag,
+ const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ gtk_text_btree_tag(start, end, tag, FALSE);
+}
+
+
+static void
+gtk_text_buffer_emit_tag(GtkTextBuffer *buffer,
+ const gchar *name,
+ gboolean apply,
+ const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ GtkTextTag *tag;
+
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+ g_return_if_fail(name != NULL);
+ g_return_if_fail(start != NULL);
+ g_return_if_fail(end != NULL);
+
+ tag = gtk_text_tag_table_lookup(buffer->tag_table,
+ name);
+
+ if (tag == NULL)
+ {
+ g_warning("Unknown tag `%s'", name);
+ return;
+ }
+
+ if (apply)
+ gtk_signal_emit(GTK_OBJECT(buffer), signals[APPLY_TAG],
+ tag, start, end);
+ else
+ gtk_signal_emit(GTK_OBJECT(buffer), signals[REMOVE_TAG],
+ tag, start, end);
+}
+
+
+void
+gtk_text_buffer_apply_tag(GtkTextBuffer *buffer,
+ const gchar *name,
+ const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ gtk_text_buffer_emit_tag(buffer, name, TRUE, start, end);
+}
+
+void
+gtk_text_buffer_remove_tag(GtkTextBuffer *buffer,
+ const gchar *name,
+ const GtkTextIter *start,
+ const GtkTextIter *end)
+
+{
+ gtk_text_buffer_emit_tag(buffer, name, FALSE, start, end);
+}
+
+void
+gtk_text_buffer_apply_tag_to_chars(GtkTextBuffer *buffer,
+ const gchar *name,
+ gint start_char, gint end_char)
+{
+ GtkTextIter start;
+ GtkTextIter end;
+
+ gtk_text_buffer_get_iter_at_char(buffer, &start, start_char);
+ gtk_text_buffer_get_iter_at_char(buffer, &end, end_char);
+
+ gtk_text_buffer_apply_tag(buffer, name, &start, &end);
+}
+
+void
+gtk_text_buffer_remove_tag_from_chars(GtkTextBuffer *buffer,
+ const gchar *name,
+ gint start_char, gint end_char)
+{
+ GtkTextIter start;
+ GtkTextIter end;
+
+ gtk_text_buffer_get_iter_at_char(buffer, &start, start_char);
+ gtk_text_buffer_get_iter_at_char(buffer, &end, end_char);
+
+ gtk_text_buffer_remove_tag(buffer, name, &start, &end);
+}
+
+/*
+ * Obtain various iterators
+ */
+
+void
+gtk_text_buffer_get_iter_at_line_char (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ gint line_number,
+ gint char_number)
+{
+ g_return_if_fail(iter != NULL);
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+
+ gtk_text_btree_get_iter_at_line_char(buffer->tree,
+ iter, line_number, char_number);
+}
+
+void
+gtk_text_buffer_get_iter_at_line (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ gint line_number)
+{
+ g_return_if_fail(iter != NULL);
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+
+ gtk_text_buffer_get_iter_at_line_char(buffer, iter, line_number, 0);
+}
+
+void
+gtk_text_buffer_get_iter_at_char (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ gint char_index)
+{
+ g_return_if_fail(iter != NULL);
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+
+ gtk_text_btree_get_iter_at_char(buffer->tree, iter, char_index);
+}
+
+void
+gtk_text_buffer_get_last_iter (GtkTextBuffer *buffer,
+ GtkTextIter *iter)
+{
+ g_return_if_fail(iter != NULL);
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+
+ gtk_text_btree_get_last_iter(buffer->tree, iter);
+}
+
+void
+gtk_text_buffer_get_bounds (GtkTextBuffer *buffer,
+ GtkTextIter *start,
+ GtkTextIter *end)
+{
+ g_return_if_fail(start != NULL);
+ g_return_if_fail(end != NULL);
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+
+ gtk_text_btree_get_iter_at_char(buffer->tree, start, 0);
+ gtk_text_btree_get_last_iter(buffer->tree, end);
+}
+
+
+gboolean
+gtk_text_buffer_get_iter_from_string(GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ const gchar *index_string)
+{
+ g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), FALSE);
+
+ return gtk_text_btree_get_iter_from_string(buffer->tree,
+ iter,
+ index_string);
+}
+
+/*
+ * Modified flag
+ */
+
+gboolean
+gtk_text_buffer_modified (GtkTextBuffer *buffer)
+{
+ g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), FALSE);
+
+ return buffer->modified;
+}
+
+void
+gtk_text_buffer_set_modified (GtkTextBuffer *buffer,
+ gboolean setting)
+{
+ gboolean fixed_setting;
+
+ g_return_if_fail(GTK_IS_TEXT_BUFFER(buffer));
+
+ fixed_setting = setting != FALSE;
+
+ if (buffer->modified == fixed_setting)
+ return;
+ else
+ {
+ buffer->modified = fixed_setting;
+ gtk_signal_emit(GTK_OBJECT(buffer), signals[MODIFIED_CHANGED]);
+ }
+}
+
+
+/*
+ * Clipboard
+ */
+
+static void
+set_clipboard_contents_nocopy(GtkTextBuffer *buffer,
+ gchar *text)
+{
+ if (text && *text == '\0')
+ {
+ g_free(text);
+ text = NULL;
+ }
+
+ if (buffer->clipboard_text != NULL)
+ g_free(buffer->clipboard_text);
+
+ buffer->clipboard_text = text;
+
+ gtk_text_buffer_update_clipboard_selection(buffer);
+}
+
+void
+gtk_text_buffer_set_clipboard_contents (GtkTextBuffer *buffer,
+ const gchar *text)
+{
+ set_clipboard_contents_nocopy(buffer, text ? g_strdup(text) : NULL);
+}
+
+const gchar*
+gtk_text_buffer_get_clipboard_contents (GtkTextBuffer *buffer)
+{
+ return buffer->clipboard_text;
+}
+
+/*
+ * Assorted other stuff
+ */
+
+gint
+gtk_text_buffer_get_line_count(GtkTextBuffer *buffer)
+{
+ g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), 0);
+
+ return gtk_text_btree_line_count(buffer->tree);
+}
+
+gint
+gtk_text_buffer_get_char_count(GtkTextBuffer *buffer)
+{
+ g_return_val_if_fail(GTK_IS_TEXT_BUFFER(buffer), 0);
+
+ return gtk_text_btree_char_count(buffer->tree);
+}
+
+GSList*
+gtk_text_buffer_get_tags (GtkTextBuffer *buffer,
+ const GtkTextIter *iter)
+{
+ GSList *retval = NULL;
+ GtkTextTag** tags;
+ gint count = 0;
+
+ tags = gtk_text_btree_get_tags(iter, &count);
+
+ if (count > 0)
+ {
+ gint i;
+
+ gtk_text_tag_array_sort(tags, count);
+
+ i = 0;
+ while (i < count)
+ {
+ retval = g_slist_prepend(retval, tags[i]);
+ ++i;
+ }
+
+ retval = g_slist_reverse(retval);
+ }
+
+ if (tags)
+ g_free(tags);
+
+ return retval;
+}
+
+/*
+ * Selection
+ */
+
+static gboolean
+have_primary_x_selection(GtkWidget *widget)
+{
+ return (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) ==
+ widget->window);
+}
+
+static gboolean
+request_primary_x_selection(GtkWidget *widget, guint32 time)
+{
+ return gtk_selection_owner_set (widget, GDK_SELECTION_PRIMARY, time);
+}
+
+static gboolean
+release_primary_x_selection(GtkWidget *widget, guint32 time)
+{
+ if (gdk_selection_owner_get (GDK_SELECTION_PRIMARY) ==
+ widget->window)
+ {
+ gtk_selection_owner_set (NULL, GDK_SELECTION_PRIMARY, time);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+
+static gboolean
+have_clipboard_x_selection(GtkWidget *widget)
+{
+ return (gdk_selection_owner_get (clipboard_atom) ==
+ widget->window);
+}
+
+static gboolean
+request_clipboard_x_selection(GtkWidget *widget, guint32 time)
+{
+ return gtk_selection_owner_set (widget, clipboard_atom, time);
+}
+
+static gboolean
+release_clipboard_x_selection(GtkWidget *widget, guint32 time)
+{
+ if (gdk_selection_owner_get (clipboard_atom) ==
+ widget->window)
+ {
+ gtk_selection_owner_set (NULL, clipboard_atom, time);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/* Called when we lose the selection */
+static gint
+selection_clear_event(GtkWidget *widget, GdkEventSelection *event,
+ gpointer data)
+{
+ GtkTextBuffer *buffer;
+
+ buffer = GTK_TEXT_BUFFER(data);
+
+ /* Let the selection handling code know that the selection
+ * has been changed, since we've overriden the default handler */
+ if (!gtk_selection_clear (widget, event))
+ return FALSE;
+
+ buffer->have_selection = FALSE;
+
+ if (event->selection == GDK_SELECTION_PRIMARY)
+ {
+ /* Move selection_bound to the insertion point */
+ GtkTextIter insert;
+ GtkTextIter selection_bound;
+
+ gtk_text_buffer_get_iter_at_mark(buffer, &insert, "insert");
+ gtk_text_buffer_get_iter_at_mark(buffer, &selection_bound, "selection_bound");
+
+ if (!gtk_text_iter_equal(&insert, &selection_bound))
+ gtk_text_buffer_move_mark(buffer, "selection_bound", &insert);
+ }
+ else if (event->selection == clipboard_atom)
+ {
+ gtk_text_buffer_set_clipboard_contents(buffer, NULL);
+ }
+
+ return TRUE;
+}
+
+/* Called when we have the selection and someone else wants our
+ data in order to paste it */
+static void
+selection_get (GtkWidget *widget,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time,
+ gpointer data)
+{
+ GtkTextBuffer *buffer;
+ gchar *str;
+ guint length;
+
+ buffer = GTK_TEXT_BUFFER(data);
+
+ if (selection_data->selection == GDK_SELECTION_PRIMARY)
+ {
+ GtkTextIter start;
+ GtkTextIter end;
+
+ if (gtk_text_buffer_get_selection_bounds(buffer, &start, &end))
+ {
+ /* Extract the selected text */
+ str = gtk_text_iter_get_visible_text(&start, &end);
+
+ length = strlen(str);
+ }
+ else
+ return;
+ }
+ else
+ {
+ const gchar *cstr;
+
+ cstr = gtk_text_buffer_get_clipboard_contents(buffer);
+
+ if (cstr == NULL)
+ return;
+
+ str = g_strdup(cstr);
+
+ length = strlen (str);
+ }
+
+ if (str)
+ {
+ if (info == TARGET_UTF8_STRING)
+ {
+ /* Pass raw UTF8 */
+ gtk_selection_data_set (selection_data,
+ utf8_atom,
+ 8*sizeof(gchar), (guchar *)str, length);
+
+ }
+ else if (info == TARGET_STRING ||
+ info == TARGET_TEXT)
+ {
+ gchar *latin1;
+
+ latin1 = gtk_text_utf_to_latin1(str, length);
+
+ gtk_selection_data_set (selection_data,
+ GDK_SELECTION_TYPE_STRING,
+ 8*sizeof(gchar), latin1, strlen(latin1));
+ g_free(latin1);
+ }
+ else if (info == TARGET_COMPOUND_TEXT)
+ {
+ /* FIXME convert UTF8 directly to current locale, not via
+ latin1 */
+
+ guchar *text;
+ GdkAtom encoding;
+ gint format;
+ gint new_length;
+ gchar *latin1;
+
+ latin1 = gtk_text_utf_to_latin1(str, length);
+
+ gdk_string_to_compound_text (latin1, &encoding, &format, &text, &new_length);
+ gtk_selection_data_set (selection_data, encoding, format, text, new_length);
+ gdk_free_compound_text (text);
+
+ g_free(latin1);
+ }
+
+ g_free (str);
+ }
+}
+
+/* Called when we request a paste and receive the data */
+static void
+selection_received (GtkWidget *widget,
+ GtkSelectionData *selection_data,
+ guint time,
+ gpointer data)
+{
+ GtkTextBuffer *buffer;
+ gboolean reselect;
+ GtkTextIter insert_point;
+ enum {INVALID, STRING, CTEXT, UTF8} type;
+
+ g_return_if_fail (widget != NULL);
+
+ buffer = GTK_TEXT_BUFFER(data);
+
+ if (selection_data->type == GDK_TARGET_STRING)
+ type = STRING;
+ else if (selection_data->type == ctext_atom)
+ type = CTEXT;
+ else if (selection_data->type == utf8_atom)
+ type = UTF8;
+ else
+ type = INVALID;
+
+ if (type == INVALID || selection_data->length < 0)
+ {
+ /* If we asked for UTF8 and didn't get it, try text; if we asked
+ for text and didn't get it, try string. If we asked for
+ anything else and didn't get it, give up. */
+ if (selection_data->target == utf8_atom)
+ gtk_selection_convert (widget, selection_data->selection,
+ GDK_TARGET_STRING, time);
+ return;
+ }
+
+ if (gtk_text_buffer_get_iter_at_mark(buffer, &insert_point,
+ "__paste_point_override"))
+ {
+ gtk_text_buffer_delete_mark(buffer, "__paste_point_override");
+ }
+ else
+ {
+ gtk_text_buffer_get_iter_at_mark(buffer, &insert_point,
+ "insert");
+ }
+
+ reselect = FALSE;
+
+ if ((TRUE/* Text is selected FIXME */) &&
+ (!buffer->have_selection ||
+ (selection_data->selection == clipboard_atom)))
+ {
+ reselect = TRUE;
+
+ if (buffer->have_selection)
+ {
+ /* FIXME Delete currently-selected chars but don't give up X
+ selection since we'll use the newly-pasted stuff as
+ a new X selection */
+
+ }
+ else
+ ; /* FIXME Delete selected chars and give up X selection */
+ }
+
+ switch (type)
+ {
+ case STRING:
+ {
+ gchar *utf;
+
+ utf = gtk_text_latin1_to_utf((const gchar*)selection_data->data,
+ selection_data->length);
+ gtk_text_buffer_insert (buffer, &insert_point,
+ utf, -1);
+ g_free(utf);
+ }
+ break;
+
+ case UTF8:
+ gtk_text_buffer_insert (buffer, &insert_point,
+ (const gchar *)selection_data->data,
+ selection_data->length);
+ break;
+
+ case CTEXT:
+ {
+ gchar **list;
+ gint count;
+ gint i;
+
+ count = gdk_text_property_to_text_list (selection_data->type,
+ selection_data->format,
+ selection_data->data,
+ selection_data->length,
+ &list);
+ for (i=0; i<count; i++)
+ {
+ /* FIXME this is broken, it assumes the CTEXT is latin1
+ when it probably isn't. */
+ gchar *utf;
+
+ utf = gtk_text_latin1_to_utf(list[i], strlen(list[i]));
+
+ gtk_text_buffer_insert(buffer, &insert_point, utf, -1);
+
+ g_free(utf);
+ }
+
+ if (count > 0)
+ gdk_free_text_list (list);
+ }
+ break;
+
+ case INVALID: /* quiet compiler */
+ break;
+ }
+
+ if (reselect)
+ ; /* FIXME Select the region of text we just inserted */
+
+}
+
+static void
+ensure_handlers(GtkTextBuffer *buffer)
+{
+ if (!buffer->selection_handlers_installed)
+ {
+ buffer->selection_handlers_installed = TRUE;
+
+ gtk_signal_connect(GTK_OBJECT(buffer->selection_widget),
+ "selection_clear_event",
+ GTK_SIGNAL_FUNC(selection_clear_event),
+ buffer);
+
+ gtk_signal_connect(GTK_OBJECT(buffer->selection_widget),
+ "selection_received",
+ GTK_SIGNAL_FUNC(selection_received),
+ buffer);
+
+ gtk_signal_connect(GTK_OBJECT(buffer->selection_widget),
+ "selection_get",
+ GTK_SIGNAL_FUNC(selection_get),
+ buffer);
+ }
+}
+
+/* FIXME GDK_CURRENT_TIME should probably go away and we should
+ figure out how to get the events in here */
+static void
+gtk_text_buffer_update_primary_selection(GtkTextBuffer *buffer)
+{
+ GtkTextIter start;
+ GtkTextIter end;
+
+ ensure_handlers(buffer);
+
+ /* Determine whether we have a selection and adjust X selection
+ accordingly. */
+
+ if (!gtk_text_buffer_get_selection_bounds(buffer, &start, &end))
+ {
+ release_primary_x_selection(buffer->selection_widget, GDK_CURRENT_TIME);
+ buffer->have_selection = FALSE;
+ }
+ else
+ {
+ /* Even if we already have the selection, we need to update our
+ timestamp. */
+ buffer->have_selection = FALSE;
+ request_primary_x_selection(buffer->selection_widget, GDK_CURRENT_TIME);
+ if (have_primary_x_selection(buffer->selection_widget))
+ buffer->have_selection = TRUE;
+ }
+}
+
+static void
+gtk_text_buffer_update_clipboard_selection(GtkTextBuffer *buffer)
+{
+ if (buffer->clipboard_text == NULL ||
+ buffer->clipboard_text[0] == '\0')
+ {
+ release_clipboard_x_selection(buffer->selection_widget, GDK_CURRENT_TIME);
+ }
+ else
+ {
+ /* Even if we already have the selection, we need to update our
+ timestamp. */
+ request_clipboard_x_selection(buffer->selection_widget, GDK_CURRENT_TIME);
+ }
+}
+
+static void
+paste(GtkTextBuffer *buffer, GdkAtom selection, guint32 time)
+{
+ ensure_handlers(buffer);
+
+ gtk_selection_convert (buffer->selection_widget, selection,
+ utf8_atom, time);
+}
+
+void
+gtk_text_buffer_paste_primary_selection(GtkTextBuffer *buffer,
+ GtkTextIter *override_location,
+ guint32 time)
+{
+ if (override_location != NULL)
+ gtk_text_buffer_create_mark(buffer,
+ "__paste_point_override",
+ override_location, FALSE);
+
+ paste(buffer, GDK_SELECTION_PRIMARY, time);
+}
+
+void
+gtk_text_buffer_paste_clipboard (GtkTextBuffer *buffer,
+ guint32 time)
+{
+ paste(buffer, clipboard_atom, time);
+}
+
+gboolean
+gtk_text_buffer_delete_selection (GtkTextBuffer *buffer)
+{
+ GtkTextIter start;
+ GtkTextIter end;
+
+ if (!gtk_text_buffer_get_selection_bounds(buffer, &start, &end))
+ {
+ return FALSE; /* No selection */
+ }
+ else
+ {
+ gtk_text_buffer_delete(buffer, &start, &end);
+ gtk_text_buffer_update_primary_selection(buffer);
+ return TRUE; /* We deleted stuff */
+ }
+}
+
+static void
+cut_or_copy(GtkTextBuffer *buffer,
+ guint32 time,
+ gboolean delete_region_after)
+{
+ /* We prefer to cut the selected region between selection_bound and
+ insertion point. If that region is empty, then we cut the region
+ between the "anchor" and the insertion point (this is for C-space
+ and M-w and other Emacs-style copy/yank keys). Note that insert
+ and selection_bound are guaranteed to exist, but the anchor only
+ exists sometimes. */
+ GtkTextIter start;
+ GtkTextIter end;
+
+ if (!gtk_text_buffer_get_selection_bounds(buffer, &start, &end))
+ {
+ /* Let's try the anchor thing */
+
+ if (!gtk_text_buffer_get_iter_at_mark(buffer, &end, "anchor"))
+ return;
+ else
+ gtk_text_iter_reorder(&start, &end);
+ }
+
+ if (!gtk_text_iter_equal(&start, &end))
+ {
+ gchar *text;
+
+ text = gtk_text_iter_get_visible_text(&start, &end);
+
+ set_clipboard_contents_nocopy(buffer, text);
+
+ if (delete_region_after)
+ gtk_text_buffer_delete(buffer, &start, &end);
+ }
+}
+
+void
+gtk_text_buffer_cut (GtkTextBuffer *buffer,
+ guint32 time)
+{
+ cut_or_copy(buffer, time, TRUE);
+}
+
+void
+gtk_text_buffer_copy (GtkTextBuffer *buffer,
+ guint32 time)
+{
+ cut_or_copy(buffer, time, FALSE);
+}
+
+
+gboolean
+gtk_text_buffer_get_selection_bounds (GtkTextBuffer *buffer,
+ GtkTextIter *start,
+ GtkTextIter *end)
+{
+ g_return_val_if_fail (buffer != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), FALSE);
+
+ return gtk_text_btree_get_selection_bounds (buffer->tree, start, end);
+}
+
+
+/*
+ * Debug spew
+ */
+
+void
+gtk_text_buffer_spew(GtkTextBuffer *buffer)
+{
+ gtk_text_btree_spew(buffer->tree);
+}
diff --git a/gtk/gtktextbuffer.h b/gtk/gtktextbuffer.h
new file mode 100644
index 000000000..66f770ab1
--- /dev/null
+++ b/gtk/gtktextbuffer.h
@@ -0,0 +1,309 @@
+#ifndef GTK_TEXT_BUFFER_H
+#define GTK_TEXT_BUFFER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * This is the PUBLIC representation of a text buffer.
+ * GtkTextBTree is the PRIVATE internal representation of it.
+ */
+
+#include <gtk/gtkwidget.h>
+#include <gtk/gtktexttagtable.h>
+#include <gtk/gtktextiter.h>
+#include <gtk/gtktextmark.h>
+
+typedef struct _GtkTextBTree GtkTextBTree;
+
+#define GTK_TYPE_TEXT_BUFFER (gtk_text_buffer_get_type())
+#define GTK_TEXT_BUFFER(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_TEXT_BUFFER, GtkTextBuffer))
+#define GTK_TEXT_BUFFER_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TEXT_BUFFER, GtkTextBufferClass))
+#define GTK_IS_TEXT_BUFFER(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_TEXT_BUFFER))
+#define GTK_IS_TEXT_BUFFER_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TEXT_BUFFER))
+#define GTK_TEXT_BUFFER_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_TEXT_BUFFER, GtkTextBufferClass))
+
+typedef struct _GtkTextBufferClass GtkTextBufferClass;
+
+struct _GtkTextBuffer {
+ GtkObject parent_instance;
+
+ GtkTextTagTable *tag_table;
+ GtkTextBTree *tree;
+
+ /* Text currently pasted to the clipboard */
+ gchar *clipboard_text;
+
+ /* Whether the buffer has been modified since last save */
+ gboolean modified;
+
+ /* We use this for selections */
+ GtkWidget *selection_widget;
+ gboolean have_selection;
+ gboolean selection_handlers_installed;
+};
+
+struct _GtkTextBufferClass {
+ GtkObjectClass parent_class;
+
+ void (* insert_text) (GtkTextBuffer *buffer,
+ GtkTextIter *pos,
+ const gchar *text,
+ gint length);
+
+
+ void (* delete_text) (GtkTextBuffer *buffer,
+ GtkTextIter *start,
+ GtkTextIter *end);
+
+ /* Only for text changed, marks/tags don't cause this
+ to be emitted */
+ void (* changed) (GtkTextBuffer *buffer);
+
+
+ /* New value for the modified flag */
+ void (* modified_changed) (GtkTextBuffer *buffer);
+
+ /* Mark moved or created */
+ void (* mark_set) (GtkTextBuffer *buffer,
+ const GtkTextIter *location,
+ const gchar *mark_name);
+
+ void (* mark_deleted) (GtkTextBuffer *buffer,
+ const gchar *mark_name);
+
+ void (* apply_tag) (GtkTextBuffer *buffer,
+ GtkTextTag *tag,
+ const GtkTextIter *start_char,
+ const GtkTextIter *end_char);
+
+ void (* remove_tag) (GtkTextBuffer *buffer,
+ GtkTextTag *tag,
+ const GtkTextIter *start_char,
+ const GtkTextIter *end_char);
+
+};
+
+GtkType gtk_text_buffer_get_type (void);
+
+
+
+/* table is NULL to create a new one */
+GtkTextBuffer *gtk_text_buffer_new (GtkTextTagTable *table);
+gint gtk_text_buffer_get_line_count (GtkTextBuffer *buffer);
+gint gtk_text_buffer_get_char_count (GtkTextBuffer *buffer);
+
+
+
+/* Insert into the buffer */
+void gtk_text_buffer_insert (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ const gchar *text,
+ gint len);
+void gtk_text_buffer_insert_at_cursor (GtkTextBuffer *buffer,
+ const gchar *text,
+ gint len);
+void gtk_text_buffer_insert_at_char (GtkTextBuffer *buffer,
+ gint char_pos,
+ const gchar *text,
+ gint len);
+void gtk_text_buffer_insert_after_line (GtkTextBuffer *buffer,
+ gint after_this_line,
+ const gchar *text,
+ gint len);
+
+
+
+/* Delete from the buffer */
+
+void gtk_text_buffer_delete (GtkTextBuffer *buffer,
+ GtkTextIter *start_iter,
+ GtkTextIter *end_iter);
+void gtk_text_buffer_delete_chars (GtkTextBuffer *buffer,
+ gint start_char,
+ gint end_char);
+void gtk_text_buffer_delete_lines (GtkTextBuffer *buffer,
+ gint start_line,
+ gint end_line);
+void gtk_text_buffer_delete_from_line (GtkTextBuffer *buffer,
+ gint line,
+ gint start_char,
+ gint end_char);
+/* Obtain strings from the buffer */
+gchar *gtk_text_buffer_get_text (GtkTextBuffer *buffer,
+ const GtkTextIter *start_iter,
+ const GtkTextIter *end_iter,
+ gboolean include_hidden_chars);
+gchar *gtk_text_buffer_get_text_chars (GtkTextBuffer *buffer,
+ gint start_char,
+ gint end_char,
+ gboolean include_hidden_chars);
+gchar *gtk_text_buffer_get_text_from_line (GtkTextBuffer *buffer,
+ gint line,
+ gint start_char,
+ gint end_char,
+ gboolean include_hidden_chars);
+gchar *gtk_text_buffer_get_slice (GtkTextBuffer *buffer,
+ const GtkTextIter *start_iter,
+ const GtkTextIter *end_iter,
+ gboolean include_hidden_chars);
+gchar *gtk_text_buffer_get_slice_chars (GtkTextBuffer *buffer,
+ gint start_char,
+ gint end_char,
+ gboolean include_hidden_chars);
+gchar *gtk_text_buffer_get_slice_from_line (GtkTextBuffer *buffer,
+ gint line,
+ gint start_char,
+ gint end_char,
+ gboolean include_hidden_chars);
+
+/* Insert a pixmap */
+void gtk_text_buffer_insert_pixmap (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ GdkPixmap *pixmap,
+ GdkBitmap *mask);
+void gtk_text_buffer_insert_pixmap_at_char (GtkTextBuffer *buffer,
+ gint char_index,
+ GdkPixmap *pixmap,
+ GdkBitmap *mask);
+
+
+
+/* Mark manipulation */
+GtkTextMark *gtk_text_buffer_create_mark (GtkTextBuffer *buffer,
+ const gchar *mark_name,
+ const GtkTextIter *where,
+ gboolean left_gravity);
+void gtk_text_buffer_move_mark (GtkTextBuffer *buffer,
+ const gchar *mark_name,
+ const GtkTextIter *where);
+void gtk_text_buffer_delete_mark (GtkTextBuffer *buffer,
+ const gchar *name);
+GtkTextMark *gtk_text_buffer_get_mark (GtkTextBuffer *buffer,
+ const gchar *name);
+
+
+/* efficiently move insert and selection_bound to same location */
+void gtk_text_buffer_place_cursor (GtkTextBuffer *buffer,
+ const GtkTextIter *where);
+
+
+
+/* Tag manipulation */
+void gtk_text_buffer_apply_tag_to_chars (GtkTextBuffer *buffer,
+ const gchar *name,
+ gint start_char,
+ gint end_char);
+void gtk_text_buffer_remove_tag_from_chars (GtkTextBuffer *buffer,
+ const gchar *name,
+ gint start_char,
+ gint end_char);
+void gtk_text_buffer_apply_tag (GtkTextBuffer *buffer,
+ const gchar *name,
+ const GtkTextIter *start_index,
+ const GtkTextIter *end_index);
+void gtk_text_buffer_remove_tag (GtkTextBuffer *buffer,
+ const gchar *name,
+ const GtkTextIter *start_index,
+ const GtkTextIter *end_index);
+
+
+
+/* You can either ignore the return value, or use it to
+ set the attributes of the tag */
+GtkTextTag *gtk_text_buffer_create_tag (GtkTextBuffer *buffer,
+ const gchar *tag_name);
+
+/* Obtain iterators pointed at various places, then you can move the
+ iterator around using the GtkTextIter operators */
+
+void gtk_text_buffer_get_iter_at_line_char (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ gint line_number,
+ gint char_number);
+void gtk_text_buffer_get_iter_at_char (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ gint char_index);
+void gtk_text_buffer_get_iter_at_line (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ gint line_number);
+void gtk_text_buffer_get_last_iter (GtkTextBuffer *buffer,
+ GtkTextIter *iter);
+void gtk_text_buffer_get_bounds (GtkTextBuffer *buffer,
+ GtkTextIter *start,
+ GtkTextIter *end);
+gboolean gtk_text_buffer_get_iter_at_mark (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ const gchar *name);
+
+
+/* There's no get_first_iter because you just get the iter for
+ line or char 0 */
+
+/*
+ Parses a string, read the man page for the Tk text widget; the only
+ variation from that is we don't support getting the index at a
+ certain pixel since the buffer has no pixel knowledge. This
+ function is mostly useful for language bindings I think.
+*/
+gboolean gtk_text_buffer_get_iter_from_string (GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ const gchar *iter_string);
+
+
+
+GSList *gtk_text_buffer_get_tags (GtkTextBuffer *buffer,
+ const GtkTextIter *iter);
+
+
+/* Used to keep track of whether the buffer needs saving; anytime the
+ buffer contents change, the modified flag is turned on. Whenever
+ you save, turn it off. Tags and marks do not affect the modified
+ flag, but if you would like them to you can connect a handler to
+ the tag/mark signals and call set_modified in your handler */
+
+gboolean gtk_text_buffer_modified (GtkTextBuffer *buffer);
+void gtk_text_buffer_set_modified (GtkTextBuffer *buffer,
+ gboolean setting);
+void gtk_text_buffer_set_clipboard_contents (GtkTextBuffer *buffer,
+ const gchar *text);
+const gchar *gtk_text_buffer_get_clipboard_contents (GtkTextBuffer *buffer);
+void gtk_text_buffer_paste_primary_selection (GtkTextBuffer *buffer,
+ GtkTextIter *override_location,
+ guint32 time);
+gboolean gtk_text_buffer_delete_selection (GtkTextBuffer *buffer);
+void gtk_text_buffer_cut (GtkTextBuffer *buffer,
+ guint32 time);
+void gtk_text_buffer_copy (GtkTextBuffer *buffer,
+ guint32 time);
+void gtk_text_buffer_paste_clipboard (GtkTextBuffer *buffer,
+ guint32 time);
+gboolean gtk_text_buffer_get_selection_bounds (GtkTextBuffer *buffer,
+ GtkTextIter *start,
+ GtkTextIter *end);
+
+
+/* This function is not implemented. */
+gboolean gtk_text_buffer_find_string(GtkTextBuffer *buffer,
+ GtkTextIter *iter,
+ const gchar *str,
+ const GtkTextIter *start,
+ const GtkTextIter *end);
+
+#if 0
+/* Waiting on glib 1.4 regexp facility */
+gboolean gtk_text_buffer_find_regexp(GtkTextBuffer *buffer,
+ GRegexp *regexp,
+ const GtkTextIter *start,
+ const GtkTextIter *end);
+#endif
+
+/* INTERNAL private stuff */
+void gtk_text_buffer_spew (GtkTextBuffer *buffer);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/gtk/gtktextchild.c b/gtk/gtktextchild.c
new file mode 100644
index 000000000..8e9c815f1
--- /dev/null
+++ b/gtk/gtktextchild.c
@@ -0,0 +1,1304 @@
+/* gtktextchild.c - child pixmaps and widgets
+ *
+ *
+ * Copyright (c) 1994 The Regents of the University of California.
+ * Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ * Copyright (c) 2000 Red Hat, Inc.
+ * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
+ *
+ * This software is copyrighted by the Regents of the University of
+ * California, Sun Microsystems, Inc., and other parties. The
+ * following terms apply to all files associated with the software
+ * unless explicitly disclaimed in individual files.
+ *
+ * The authors hereby grant permission to use, copy, modify,
+ * distribute, and license this software and its documentation for any
+ * purpose, provided that existing copyright notices are retained in
+ * all copies and that this notice is included verbatim in any
+ * distributions. No written agreement, license, or royalty fee is
+ * required for any of the authorized uses. Modifications to this
+ * software may be copyrighted by their authors and need not follow
+ * the licensing terms described here, provided that the new terms are
+ * clearly indicated on the first page of each file where they apply.
+ *
+ * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+ * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
+ * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
+ * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
+ * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
+ * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * GOVERNMENT USE: If you are acquiring this software on behalf of the
+ * U.S. government, the Government shall have only "Restricted Rights"
+ * in the software and related documentation as defined in the Federal
+ * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
+ * are acquiring the software on behalf of the Department of Defense,
+ * the software shall be classified as "Commercial Computer Software"
+ * and the Government shall have only "Restricted Rights" as defined
+ * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
+ * foregoing, the authors grant the U.S. Government and others acting
+ * in its behalf permission to use and distribute the software in
+ * accordance with the terms specified in this license.
+ *
+ */
+
+#include "gtktextchild.h"
+#include "gtktextbtree.h"
+
+static GtkTextLineSegment *
+pixmap_segment_cleanup_func(GtkTextLineSegment *seg,
+ GtkTextLine *line)
+{
+ /* nothing */
+ return seg;
+}
+
+static int
+pixmap_segment_delete_func(GtkTextLineSegment *seg,
+ GtkTextLine *line,
+ gboolean tree_gone)
+{
+ if (seg->body.pixmap.pixmap)
+ gdk_pixmap_unref(seg->body.pixmap.pixmap);
+
+ if (seg->body.pixmap.mask)
+ gdk_bitmap_unref(seg->body.pixmap.mask);
+
+ g_free(seg);
+
+ return 0;
+}
+
+static void
+pixmap_segment_check_func(GtkTextLineSegment *seg,
+ GtkTextLine *line)
+{
+ if (seg->next == NULL)
+ g_error("pixmap segment is the last segment in a line");
+
+ if (seg->byte_count != 3)
+ g_error("pixmap segment has byte count of %d", seg->byte_count);
+
+ if (seg->char_count != 1)
+ g_error("pixmap segment has char count of %d", seg->char_count);
+}
+
+
+GtkTextLineSegmentClass gtk_text_pixmap_type = {
+ "pixmap", /* name */
+ 0, /* leftGravity */
+ NULL, /* splitFunc */
+ pixmap_segment_delete_func, /* deleteFunc */
+ pixmap_segment_cleanup_func, /* cleanupFunc */
+ NULL, /* lineChangeFunc */
+ pixmap_segment_check_func /* checkFunc */
+
+};
+
+#if 0
+GtkTextLineSegmentClass gtk_text_view_child_type = {
+ "child-widget", /* name */
+ 0, /* leftGravity */
+ child_segment_split_func, /* splitFunc */
+ child_segment_delete_func, /* deleteFunc */
+ child_segment_cleanup_func, /* cleanupFunc */
+ NULL, /* lineChangeFunc */
+ child_segment_check_func /* checkFunc */
+};
+#endif
+
+#define PIXMAP_SEG_SIZE ((unsigned) (G_STRUCT_OFFSET(GtkTextLineSegment, body) \
+ + sizeof(GtkTextPixmap)))
+
+GtkTextLineSegment *
+gtk_text_pixmap_segment_new(GdkPixmap *pixmap, GdkBitmap *mask)
+{
+ GtkTextLineSegment *seg;
+
+ seg = g_malloc(PIXMAP_SEG_SIZE);
+
+ seg->type = &gtk_text_pixmap_type;
+
+ seg->next = NULL;
+
+ seg->byte_count = 3; /* We convert to the 0xFFFD "unknown character",
+ a 3-byte sequence in UTF-8 */
+ seg->char_count = 1;
+
+ seg->body.pixmap.pixmap = pixmap;
+ seg->body.pixmap.mask = mask;
+
+ if (pixmap)
+ gdk_pixmap_ref(pixmap);
+
+ if (mask)
+ gdk_bitmap_ref(mask);
+
+ return seg;
+}
+
+#if 0
+
+/*
+ * The following structure is the official type record for the
+ * embedded window geometry manager:
+ */
+
+static void EmbWinRequestFunc _ANSI_ARGS_((gpointer clientData,
+ Tk_Window tkwin));
+static void EmbWinLostSlaveFunc _ANSI_ARGS_((gpointer clientData,
+ Tk_Window tkwin));
+
+static Tk_GeomMgr textGeomType = {
+ "text", /* name */
+ EmbWinRequestFunc, /* requestFunc */
+ EmbWinLostSlaveFunc, /* lostSlaveFunc */
+};
+
+/*
+ * Definitions for alignment values:
+ */
+
+#define ALIGN_BOTTOM 0
+#define ALIGN_CENTER 1
+#define ALIGN_TOP 2
+#define ALIGN_BASELINE 3
+
+/*
+ * Macro that determines the size of an embedded window segment:
+ */
+
+#define EW_SEG_SIZE ((unsigned) (G_STRUCT_OFFSET(GtkTextLineSegment, body) \
+ + sizeof(GtkTextEmbWindow)))
+
+/*
+ * Prototypes for procedures defined in this file:
+ */
+
+static int AlignParseFunc _ANSI_ARGS_((gpointer clientData,
+ Tcl_Interp *interp, Tk_Window tkwin, char *value,
+ char *widgRec, int offset));
+static char * AlignPrintFunc _ANSI_ARGS_((gpointer clientData,
+ Tk_Window tkwin, char *widgRec, int offset,
+ Tcl_FreeFunc **freeFuncPtr));
+static GtkTextLineSegment * EmbWinCleanupFunc _ANSI_ARGS_((GtkTextLineSegment *segPtr,
+ GtkTextLine *line));
+static void EmbWinCheckFunc _ANSI_ARGS_((GtkTextLineSegment *segPtr,
+ GtkTextLine *line));
+static void EmbWinBboxFunc _ANSI_ARGS_((GtkTextDisplayChunk *chunkPtr,
+ int index, int y, int lineHeight, int baseline,
+ int *xPtr, int *yPtr, int *widthPtr,
+ int *heightPtr));
+static int EmbWinConfigure _ANSI_ARGS_((GtkTextView *tkxt,
+ GtkTextLineSegment *ewPtr, int argc, char **argv));
+static void EmbWinDelayedUnmap _ANSI_ARGS_((
+ gpointer clientData));
+static int EmbWinDeleteFunc _ANSI_ARGS_((GtkTextLineSegment *segPtr,
+ GtkTextLine *line, int treeGone));
+static void EmbWinDisplayFunc _ANSI_ARGS_((
+ GtkTextDisplayChunk *chunkPtr, int x, int y,
+ int lineHeight, int baseline, Display *display,
+ Drawable dst, int screenY));
+static int EmbWinLayoutFunc _ANSI_ARGS_((GtkTextView *tkxt,
+ GtkTextIndex *indexPtr, GtkTextLineSegment *segPtr,
+ int offset, int maxX, int maxChars,
+ int noCharsYet, GtkWrapMode wrapMode,
+ GtkTextDisplayChunk *chunkPtr));
+static void EmbWinStructureFunc _ANSI_ARGS_((gpointer clientData,
+ XEvent *eventPtr));
+static void EmbWinUndisplayFunc _ANSI_ARGS_((GtkTextView *tkxt,
+ GtkTextDisplayChunk *chunkPtr));
+
+/*
+ * The following structure declares the "embedded window" segment type.
+ */
+
+static GtkTextLineSegmentClass tkTextEmbWindowType = {
+ "window", /* name */
+ 0, /* leftGravity */
+ (GtkTextLineSegmentSplitFunc *) NULL, /* splitFunc */
+ EmbWinDeleteFunc, /* deleteFunc */
+ EmbWinCleanupFunc, /* cleanupFunc */
+ (GtkTextLineSegmentLineChangeFunc *) NULL, /* lineChangeFunc */
+ EmbWinLayoutFunc, /* layoutFunc */
+ EmbWinCheckFunc /* checkFunc */
+};
+
+/*
+ * Information used for parsing window configuration options:
+ */
+
+static Tk_CustomOption alignOption = {AlignParseFunc, AlignPrintFunc,
+ (gpointer) NULL};
+
+static Tk_ConfigSpec configSpecs[] = {
+ {TK_CONFIG_CUSTOM, "-align", (char *) NULL, (char *) NULL,
+ "center", 0, TK_CONFIG_DONT_SET_DEFAULT, &alignOption},
+ {TK_CONFIG_STRING, "-create", (char *) NULL, (char *) NULL,
+ (char *) NULL, G_STRUCT_OFFSET(GtkTextEmbWindow, create),
+ TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK},
+ {TK_CONFIG_INT, "-padx", (char *) NULL, (char *) NULL,
+ "0", G_STRUCT_OFFSET(GtkTextEmbWindow, padX),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_INT, "-pady", (char *) NULL, (char *) NULL,
+ "0", G_STRUCT_OFFSET(GtkTextEmbWindow, padY),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_BOOLEAN, "-stretch", (char *) NULL, (char *) NULL,
+ "0", G_STRUCT_OFFSET(GtkTextEmbWindow, stretch),
+ TK_CONFIG_DONT_SET_DEFAULT},
+ {TK_CONFIG_WINDOW, "-window", (char *) NULL, (char *) NULL,
+ (char *) NULL, G_STRUCT_OFFSET(GtkTextEmbWindow, tkwin),
+ TK_CONFIG_DONT_SET_DEFAULT|TK_CONFIG_NULL_OK},
+ {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
+ (char *) NULL, 0, 0}
+};
+
+/*
+ *--------------------------------------------------------------
+ *
+ * GtkTextViewWindowCmd --
+ *
+ * This procedure implements the "window" widget command
+ * for text widgets. See the user documentation for details
+ * on what it does.
+ *
+ * Results:
+ * A standard Tcl result or error.
+ *
+ * Side effects:
+ * See the user documentation.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+GtkTextViewWindowCmd(tkxt, interp, argc, argv)
+ GtkTextView *tkxt; /* Information about text widget. */
+ Tcl_Interp *interp; /* Current interpreter. */
+ int argc; /* Number of arguments. */
+ char **argv; /* Argument strings. Someone else has already
+ * parsed this command enough to know that
+ * argv[1] is "window". */
+{
+ size_t length;
+ GtkTextLineSegment *ewPtr;
+
+ if (argc < 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " window option ?arg arg ...?\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ length = strlen(argv[2]);
+ if ((strncmp(argv[2], "cget", length) == 0) && (length >= 2)) {
+ GtkTextIndex index;
+ GtkTextLineSegment *ewPtr;
+
+ if (argc != 5) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " window cget index option\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (gtk_text_btree_index_from_string(interp, tkxt, argv[3], &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ ewPtr = gtk_text_view_index_to_seg(&index, (int *) NULL);
+ if (ewPtr->type != &tkTextEmbWindowType) {
+ Tcl_AppendResult(interp, "no embedded window at index \"",
+ argv[3], "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ return Tk_ConfigureValue(interp, tkxt->tkwin, configSpecs,
+ (char *) &ewPtr->body.ew, argv[4], 0);
+ } else if ((strncmp(argv[2], "configure", length) == 0) && (length >= 2)) {
+ GtkTextIndex index;
+ GtkTextLineSegment *ewPtr;
+
+ if (argc < 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " window configure index ?option value ...?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (gtk_text_btree_index_from_string(interp, tkxt, argv[3], &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ ewPtr = gtk_text_view_index_to_seg(&index, (int *) NULL);
+ if (ewPtr->type != &tkTextEmbWindowType) {
+ Tcl_AppendResult(interp, "no embedded window at index \"",
+ argv[3], "\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (argc == 4) {
+ return Tk_ConfigureInfo(interp, tkxt->tkwin, configSpecs,
+ (char *) &ewPtr->body.ew, (char *) NULL, 0);
+ } else if (argc == 5) {
+ return Tk_ConfigureInfo(interp, tkxt->tkwin, configSpecs,
+ (char *) &ewPtr->body.ew, argv[4], 0);
+ } else {
+ gtk_text_buffer_need_redisplay(tree->buffer, &index, &index);
+ return EmbWinConfigure(tkxt, ewPtr, argc-4, argv+4);
+ }
+ } else if ((strncmp(argv[2], "create", length) == 0) && (length >= 2)) {
+ GtkTextIndex index;
+ int lineIndex;
+
+ /*
+ * Add a new window. Find where to put the new window, and
+ * mark that position for redisplay.
+ */
+
+ if (argc < 4) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " window create index ?option value ...?\"",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ if (gtk_text_btree_index_from_string(interp, tkxt, argv[3], &index) != TCL_OK) {
+ return TCL_ERROR;
+ }
+
+ /*
+ * Don't allow insertions on the last (dummy) line of the text.
+ */
+
+ lineIndex = gtk_text_btree_line_number(index.line);
+ if (lineIndex == gtk_text_btree_num_lines(tkxt->tree)) {
+ lineIndex--;
+ gtk_text_btree_get_line_byte_index(tkxt->tree, lineIndex, G_MAXINT, &index);
+ }
+
+ /*
+ * Create the new window segment and initialize it.
+ */
+
+ ewPtr = (GtkTextLineSegment *) g_malloc(EW_SEG_SIZE);
+ ewPtr->type = &tkTextEmbWindowType;
+ ewPtr->size = 1;
+ ewPtr->body.ew.tkxt = tkxt;
+ ewPtr->body.ew.line = NULL;
+ ewPtr->body.ew.tkwin = NULL;
+ ewPtr->body.ew.create = NULL;
+ ewPtr->body.ew.align = ALIGN_CENTER;
+ ewPtr->body.ew.padX = ewPtr->body.ew.padY = 0;
+ ewPtr->body.ew.stretch = 0;
+ ewPtr->body.ew.chunkCount = 0;
+ ewPtr->body.ew.displayed = 0;
+
+ /*
+ * Link the segment into the text widget, then configure it (delete
+ * it again if the configuration fails).
+ */
+
+ gtk_text_buffer_need_redisplay(tkxt, &index, &index);
+ gtk_text_btree_link_segment(ewPtr, &index);
+ if (EmbWinConfigure(tkxt, ewPtr, argc-4, argv+4) != TCL_OK) {
+ GtkTextIndex index2;
+
+ gtk_text_view_index_forw_chars(&index, 1, &index2);
+ gtk_text_btree_delete_chars(&index, &index2);
+ return TCL_ERROR;
+ }
+ } else if (strncmp(argv[2], "names", length) == 0) {
+ Tcl_HashSearch search;
+ Tcl_HashEntry *hPtr;
+
+ if (argc != 3) {
+ Tcl_AppendResult(interp, "wrong # args: should be \"",
+ argv[0], " window names\"", (char *) NULL);
+ return TCL_ERROR;
+ }
+ for (hPtr = Tcl_FirstHashEntry(&tkxt->windowTable, &search);
+ hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
+ Tcl_AppendElement(interp,
+ Tcl_GetHashKey(&tkxt->markTable, hPtr));
+ }
+ } else {
+ Tcl_AppendResult(interp, "bad window option \"", argv[2],
+ "\": must be cget, configure, create, or names",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EmbWinConfigure --
+ *
+ * This procedure is called to handle configuration options
+ * for an embedded window, using an argc/argv list.
+ *
+ * Results:
+ * The return value is a standard Tcl result. If TCL_ERROR is
+ * returned, then the interp's result contains an error message..
+ *
+ * Side effects:
+ * Configuration information for the embedded window changes,
+ * such as alignment, stretching, or name of the embedded
+ * window.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+EmbWinConfigure(tkxt, ewPtr, argc, argv)
+ GtkTextView *tkxt; /* Information about text widget that
+ * contains embedded window. */
+ GtkTextLineSegment *ewPtr; /* Embedded window to be configured. */
+ int argc; /* Number of strings in argv. */
+ char **argv; /* Array of strings describing configuration
+ * options. */
+{
+ Tk_Window oldWindow;
+ Tcl_HashEntry *hPtr;
+ int new;
+
+ oldWindow = ewPtr->body.ew.tkwin;
+ if (Tk_ConfigureWidget(tkxt->interp, tkxt->tkwin, configSpecs,
+ argc, argv, (char *) &ewPtr->body.ew, TK_CONFIG_ARGV_ONLY)
+ != TCL_OK) {
+ return TCL_ERROR;
+ }
+ if (oldWindow != ewPtr->body.ew.tkwin) {
+ if (oldWindow != NULL) {
+ Tcl_DeleteHashEntry(Tcl_FindHashEntry(&tkxt->windowTable,
+ Tk_PathName(oldWindow)));
+ Tk_DeleteEventHandler(oldWindow, StructureNotifyMask,
+ EmbWinStructureFunc, (gpointer) ewPtr);
+ Tk_ManageGeometry(oldWindow, (Tk_GeomMgr *) NULL,
+ (gpointer) NULL);
+ if (tkxt->tkwin != Tk_Parent(oldWindow)) {
+ Tk_UnmaintainGeometry(oldWindow, tkxt->tkwin);
+ } else {
+ Tk_UnmapWindow(oldWindow);
+ }
+ }
+ if (ewPtr->body.ew.tkwin != NULL) {
+ Tk_Window ancestor, parent;
+
+ /*
+ * Make sure that the text is either the parent of the
+ * embedded window or a descendant of that parent. Also,
+ * don't allow a top-level window to be managed inside
+ * a text.
+ */
+
+ parent = Tk_Parent(ewPtr->body.ew.tkwin);
+ for (ancestor = tkxt->tkwin; ;
+ ancestor = Tk_Parent(ancestor)) {
+ if (ancestor == parent) {
+ break;
+ }
+ if (Tk_IsTopLevel(ancestor)) {
+ badMaster:
+ Tcl_AppendResult(tkxt->interp, "can't embed ",
+ Tk_PathName(ewPtr->body.ew.tkwin), " in ",
+ Tk_PathName(tkxt->tkwin), (char *) NULL);
+ ewPtr->body.ew.tkwin = NULL;
+ return TCL_ERROR;
+ }
+ }
+ if (Tk_IsTopLevel(ewPtr->body.ew.tkwin)
+ || (ewPtr->body.ew.tkwin == tkxt->tkwin)) {
+ goto badMaster;
+ }
+
+ /*
+ * Take over geometry management for the window, plus create
+ * an event handler to find out when it is deleted.
+ */
+
+ Tk_ManageGeometry(ewPtr->body.ew.tkwin, &textGeomType,
+ (gpointer) ewPtr);
+ Tk_CreateEventHandler(ewPtr->body.ew.tkwin, StructureNotifyMask,
+ EmbWinStructureFunc, (gpointer) ewPtr);
+
+ /*
+ * Special trick! Must enter into the hash table *after*
+ * calling Tk_ManageGeometry: if the window was already managed
+ * elsewhere in this text, the Tk_ManageGeometry call will cause
+ * the entry to be removed, which could potentially lose the new
+ * entry.
+ */
+
+ hPtr = Tcl_CreateHashEntry(&tkxt->windowTable,
+ Tk_PathName(ewPtr->body.ew.tkwin), &new);
+ Tcl_SetHashValue(hPtr, ewPtr);
+
+ }
+ }
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * AlignParseFunc --
+ *
+ * This procedure is invoked by Tk_ConfigureWidget during
+ * option processing to handle "-align" options for embedded
+ * windows.
+ *
+ * Results:
+ * A standard Tcl return value.
+ *
+ * Side effects:
+ * The alignment for the embedded window may change.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static int
+AlignParseFunc(clientData, interp, tkwin, value, widgRec, offset)
+ gpointer clientData; /* Not used.*/
+ Tcl_Interp *interp; /* Used for reporting errors. */
+ Tk_Window tkwin; /* Window for text widget. */
+ char *value; /* Value of option. */
+ char *widgRec; /* Pointer to GtkTextEmbWindow
+ * structure. */
+ int offset; /* Offset into item (ignored). */
+{
+ GtkTextEmbWindow *embPtr = (GtkTextEmbWindow *) widgRec;
+
+ if (strcmp(value, "baseline") == 0) {
+ embPtr->align = ALIGN_BASELINE;
+ } else if (strcmp(value, "bottom") == 0) {
+ embPtr->align = ALIGN_BOTTOM;
+ } else if (strcmp(value, "center") == 0) {
+ embPtr->align = ALIGN_CENTER;
+ } else if (strcmp(value, "top") == 0) {
+ embPtr->align = ALIGN_TOP;
+ } else {
+ Tcl_AppendResult(interp, "bad alignment \"", value,
+ "\": must be baseline, bottom, center, or top",
+ (char *) NULL);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * AlignPrintFunc --
+ *
+ * This procedure is invoked by the Tk configuration code
+ * to produce a printable string for the "-align" configuration
+ * option for embedded windows.
+ *
+ * Results:
+ * The return value is a string describing the embedded
+ * window's current alignment.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static char *
+AlignPrintFunc(clientData, tkwin, widgRec, offset, freeFuncPtr)
+ gpointer clientData; /* Ignored. */
+ Tk_Window tkwin; /* Window for text widget. */
+ char *widgRec; /* Pointer to GtkTextEmbWindow
+ * structure. */
+ int offset; /* Ignored. */
+ Tcl_FreeFunc **freeFuncPtr; /* Pointer to variable to fill in with
+ * information about how to reclaim
+ * storage for return string. */
+{
+ switch (((GtkTextEmbWindow *) widgRec)->align) {
+ case ALIGN_BASELINE:
+ return "baseline";
+ case ALIGN_BOTTOM:
+ return "bottom";
+ case ALIGN_CENTER:
+ return "center";
+ case ALIGN_TOP:
+ return "top";
+ default:
+ return "??";
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EmbWinStructureFunc --
+ *
+ * This procedure is invoked by the Tk event loop whenever
+ * StructureNotify events occur for a window that's embedded
+ * in a text widget. This procedure's only purpose is to
+ * clean up when windows are deleted.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window is disassociated from the window segment, and
+ * the portion of the text is redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+EmbWinStructureFunc(clientData, eventPtr)
+ gpointer clientData; /* Pointer to record describing window item. */
+ XEvent *eventPtr; /* Describes what just happened. */
+{
+ GtkTextLineSegment *ewPtr = (GtkTextLineSegment *) clientData;
+ GtkTextIndex index;
+
+ if (eventPtr->type != DestroyNotify) {
+ return;
+ }
+
+ Tcl_DeleteHashEntry(Tcl_FindHashEntry(&ewPtr->body.ew.tkxt->windowTable,
+ Tk_PathName(ewPtr->body.ew.tkwin)));
+ ewPtr->body.ew.tkwin = NULL;
+ index.tree = ewPtr->body.ew.tkxt->tree;
+ index.line = ewPtr->body.ew.line;
+ index.byteIndex = gtk_text_line_segment_get_offset(ewPtr, ewPtr->body.ew.line);
+ gtk_text_buffer_need_redisplay(ewPtr->body.ew.tkxt, &index, &index);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EmbWinRequestFunc --
+ *
+ * This procedure is invoked whenever a window that's associated
+ * with a window canvas item changes its requested dimensions.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The size and location on the screen of the window may change,
+ * depending on the options specified for the window item.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static void
+EmbWinRequestFunc(clientData, tkwin)
+ gpointer clientData; /* Pointer to record for window item. */
+ Tk_Window tkwin; /* Window that changed its desired
+ * size. */
+{
+ GtkTextLineSegment *ewPtr = (GtkTextLineSegment *) clientData;
+ GtkTextIndex index;
+
+ index.tree = ewPtr->body.ew.tkxt->tree;
+ index.line = ewPtr->body.ew.line;
+ index.byteIndex = gtk_text_line_segment_get_offset(ewPtr, ewPtr->body.ew.line);
+ gtk_text_buffer_need_redisplay(ewPtr->body.ew.tkxt, &index, &index);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EmbWinLostSlaveFunc --
+ *
+ * This procedure is invoked by the Tk geometry manager when
+ * a slave window managed by a text widget is claimed away
+ * by another geometry manager.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window is disassociated from the window segment, and
+ * the portion of the text is redisplayed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+EmbWinLostSlaveFunc(clientData, tkwin)
+ gpointer clientData; /* Pointer to record describing window item. */
+ Tk_Window tkwin; /* Window that was claimed away by another
+ * geometry manager. */
+{
+ GtkTextLineSegment *ewPtr = (GtkTextLineSegment *) clientData;
+ GtkTextIndex index;
+
+ Tk_DeleteEventHandler(ewPtr->body.ew.tkwin, StructureNotifyMask,
+ EmbWinStructureFunc, (gpointer) ewPtr);
+ Tcl_CancelIdleCall(EmbWinDelayedUnmap, (gpointer) ewPtr);
+ if (ewPtr->body.ew.tkxt->tkwin != Tk_Parent(tkwin)) {
+ Tk_UnmaintainGeometry(tkwin, ewPtr->body.ew.tkxt->tkwin);
+ } else {
+ Tk_UnmapWindow(tkwin);
+ }
+ Tcl_DeleteHashEntry(Tcl_FindHashEntry(&ewPtr->body.ew.tkxt->windowTable,
+ Tk_PathName(ewPtr->body.ew.tkwin)));
+ ewPtr->body.ew.tkwin = NULL;
+ index.tree = ewPtr->body.ew.tkxt->tree;
+ index.line = ewPtr->body.ew.line;
+ index.byteIndex = gtk_text_line_segment_get_offset(ewPtr, ewPtr->body.ew.line);
+ gtk_text_buffer_need_redisplay(ewPtr->body.ew.tkxt, &index, &index);
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EmbWinDeleteFunc --
+ *
+ * This procedure is invoked by the text B-tree code whenever
+ * an embedded window lies in a range of characters being deleted.
+ *
+ * Results:
+ * Returns 0 to indicate that the deletion has been accepted.
+ *
+ * Side effects:
+ * The embedded window is deleted, if it exists, and any resources
+ * associated with it are released.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static int
+EmbWinDeleteFunc(ewPtr, line, treeGone)
+ GtkTextLineSegment *ewPtr; /* Segment being deleted. */
+ GtkTextLine *line; /* Line containing segment. */
+ int treeGone; /* Non-zero means the entire tree is
+ * being deleted, so everything must
+ * get cleaned up. */
+{
+ Tcl_HashEntry *hPtr;
+
+ if (ewPtr->body.ew.tkwin != NULL) {
+ hPtr = Tcl_FindHashEntry(&ewPtr->body.ew.tkxt->windowTable,
+ Tk_PathName(ewPtr->body.ew.tkwin));
+ if (hPtr != NULL) {
+ /*
+ * (It's possible for there to be no hash table entry for this
+ * window, if an error occurred while creating the window segment
+ * but before the window got added to the table)
+ */
+
+ Tcl_DeleteHashEntry(hPtr);
+ }
+
+ /*
+ * Delete the event handler for the window before destroying
+ * the window, so that EmbWinStructureFunc doesn't get called
+ * (we'll already do everything that it would have done, and
+ * it will just get confused).
+ */
+
+ Tk_DeleteEventHandler(ewPtr->body.ew.tkwin, StructureNotifyMask,
+ EmbWinStructureFunc, (gpointer) ewPtr);
+ Tk_DestroyWindow(ewPtr->body.ew.tkwin);
+ }
+ Tcl_CancelIdleCall(EmbWinDelayedUnmap, (gpointer) ewPtr);
+ Tk_FreeOptions(configSpecs, (char *) &ewPtr->body.ew,
+ ewPtr->body.ew.tkxt->display, 0);
+ g_free((char *) ewPtr);
+ return 0;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EmbWinCleanupFunc --
+ *
+ * This procedure is invoked by the B-tree code whenever a
+ * segment containing an embedded window is moved from one
+ * line to another.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The line field of the segment gets updated.
+ *
+ *--------------------------------------------------------------
+ */
+
+static GtkTextLineSegment *
+EmbWinCleanupFunc(ewPtr, line)
+ GtkTextLineSegment *ewPtr; /* Mark segment that's being moved. */
+ GtkTextLine *line; /* Line that now contains segment. */
+{
+ ewPtr->body.ew.line = line;
+ return ewPtr;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EmbWinLayoutFunc --
+ *
+ * This procedure is the "layoutFunc" for embedded window
+ * segments.
+ *
+ * Results:
+ * 1 is returned to indicate that the segment should be
+ * displayed. The chunkPtr structure is filled in.
+ *
+ * Side effects:
+ * None, except for filling in chunkPtr.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /*ARGSUSED*/
+static int
+EmbWinLayoutFunc(tkxt, indexPtr, ewPtr, offset, maxX, maxChars,
+ noCharsYet, wrapMode, chunkPtr)
+ GtkTextView *tkxt; /* Text widget being layed out. */
+ GtkTextIndex *indexPtr; /* Identifies first character in chunk. */
+ GtkTextLineSegment *ewPtr; /* Segment corresponding to indexPtr. */
+ int offset; /* Offset within segPtr corresponding to
+ * indexPtr (always 0). */
+ int maxX; /* Chunk must not occupy pixels at this
+ * position or higher. */
+ int maxChars; /* Chunk must not include more than this
+ * many characters. */
+ int noCharsYet; /* Non-zero means no characters have been
+ * assigned to this line yet. */
+ GtkWrapMode wrapMode; /* Wrap mode to use for line: GTK_WRAPMODE_CHAR,
+ * GTK_WRAPMODE_NONE, or GTK_WRAPMODE_WORD. */
+ GtkTextDisplayChunk *chunkPtr;
+ /* Structure to fill in with information
+ * about this chunk. The x field has already
+ * been set by the caller. */
+{
+ int width, height;
+
+ if (offset != 0) {
+ panic("Non-zero offset in EmbWinLayoutFunc");
+ }
+
+ if ((ewPtr->body.ew.tkwin == NULL) && (ewPtr->body.ew.create != NULL)) {
+ int code, new;
+ Tcl_DString name;
+ Tk_Window ancestor;
+ Tcl_HashEntry *hPtr;
+
+ /*
+ * The window doesn't currently exist. Create it by evaluating
+ * the creation script. The script must return the window's
+ * path name: look up that name to get back to the window
+ * token. Then ourselves as the geometry manager for
+ * the window.
+ */
+
+ code = Tcl_GlobalEval(tkxt->interp, ewPtr->body.ew.create);
+ if (code != TCL_OK) {
+ createError:
+ Tcl_BackgroundError(tkxt->interp);
+ goto gotWindow;
+ }
+ Tcl_DStringInit(&name);
+ Tcl_DStringAppend(&name, Tcl_GetStringResult(tkxt->interp), -1);
+ Tcl_ResetResult(tkxt->interp);
+ ewPtr->body.ew.tkwin = Tk_NameToWindow(tkxt->interp,
+ Tcl_DStringValue(&name), tkxt->tkwin);
+ if (ewPtr->body.ew.tkwin == NULL) {
+ goto createError;
+ }
+ for (ancestor = tkxt->tkwin; ;
+ ancestor = Tk_Parent(ancestor)) {
+ if (ancestor == Tk_Parent(ewPtr->body.ew.tkwin)) {
+ break;
+ }
+ if (Tk_IsTopLevel(ancestor)) {
+ badMaster:
+ Tcl_AppendResult(tkxt->interp, "can't embed ",
+ Tk_PathName(ewPtr->body.ew.tkwin), " relative to ",
+ Tk_PathName(tkxt->tkwin), (char *) NULL);
+ Tcl_BackgroundError(tkxt->interp);
+ ewPtr->body.ew.tkwin = NULL;
+ goto gotWindow;
+ }
+ }
+ if (Tk_IsTopLevel(ewPtr->body.ew.tkwin)
+ || (tkxt->tkwin == ewPtr->body.ew.tkwin)) {
+ goto badMaster;
+ }
+ Tk_ManageGeometry(ewPtr->body.ew.tkwin, &textGeomType,
+ (gpointer) ewPtr);
+ Tk_CreateEventHandler(ewPtr->body.ew.tkwin, StructureNotifyMask,
+ EmbWinStructureFunc, (gpointer) ewPtr);
+
+ /*
+ * Special trick! Must enter into the hash table *after*
+ * calling Tk_ManageGeometry: if the window was already managed
+ * elsewhere in this text, the Tk_ManageGeometry call will cause
+ * the entry to be removed, which could potentially lose the new
+ * entry.
+ */
+
+ hPtr = Tcl_CreateHashEntry(&tkxt->windowTable,
+ Tk_PathName(ewPtr->body.ew.tkwin), &new);
+ Tcl_SetHashValue(hPtr, ewPtr);
+ }
+
+ /*
+ * See if there's room for this window on this line.
+ */
+
+ gotWindow:
+ if (ewPtr->body.ew.tkwin == NULL) {
+ width = 0;
+ height = 0;
+ } else {
+ width = Tk_ReqWidth(ewPtr->body.ew.tkwin) + 2*ewPtr->body.ew.padX;
+ height = Tk_ReqHeight(ewPtr->body.ew.tkwin) + 2*ewPtr->body.ew.padY;
+ }
+ if ((width > (maxX - chunkPtr->x))
+ && !noCharsYet && (tkxt->wrapMode != GTK_WRAPMODE_NONE)) {
+ return 0;
+ }
+
+ /*
+ * Fill in the chunk structure.
+ */
+
+ chunkPtr->displayFunc = EmbWinDisplayFunc;
+ chunkPtr->undisplayFunc = EmbWinUndisplayFunc;
+ chunkPtr->measureFunc = (GtkTextViewChunkMeasureFunc *) NULL;
+ chunkPtr->bboxFunc = EmbWinBboxFunc;
+ chunkPtr->numBytes = 1;
+ if (ewPtr->body.ew.align == ALIGN_BASELINE) {
+ chunkPtr->minAscent = height - ewPtr->body.ew.padY;
+ chunkPtr->minDescent = ewPtr->body.ew.padY;
+ chunkPtr->minHeight = 0;
+ } else {
+ chunkPtr->minAscent = 0;
+ chunkPtr->minDescent = 0;
+ chunkPtr->minHeight = height;
+ }
+ chunkPtr->width = width;
+ chunkPtr->breakIndex = -1;
+ chunkPtr->breakIndex = 1;
+ chunkPtr->clientData = (gpointer) ewPtr;
+ ewPtr->body.ew.chunkCount += 1;
+ return 1;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EmbWinCheckFunc --
+ *
+ * This procedure is invoked by the B-tree code to perform
+ * consistency checks on embedded windows.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The procedure panics if it detects anything wrong with
+ * the embedded window.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+EmbWinCheckFunc(ewPtr, line)
+ GtkTextLineSegment *ewPtr; /* Segment to check. */
+ GtkTextLine *line; /* Line containing segment. */
+{
+ if (ewPtr->next == NULL) {
+ panic("EmbWinCheckFunc: embedded window is last segment in line");
+ }
+ if (ewPtr->size != 1) {
+ panic("EmbWinCheckFunc: embedded window has size %d", ewPtr->size);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EmbWinDisplayFunc --
+ *
+ * This procedure is invoked by the text displaying code
+ * when it is time to actually draw an embedded window
+ * chunk on the screen.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The embedded window gets moved to the correct location
+ * and mapped onto the screen.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+EmbWinDisplayFunc(chunkPtr, x, y, lineHeight, baseline, display, dst, screenY)
+ GtkTextDisplayChunk *chunkPtr; /* Chunk that is to be drawn. */
+ int x; /* X-position in dst at which to
+ * draw this chunk (differs from
+ * the x-position in the chunk because
+ * of scrolling). */
+ int y; /* Top of rectangular bounding box
+ * for line: tells where to draw this
+ * chunk in dst (x-position is in
+ * the chunk itself). */
+ int lineHeight; /* Total height of line. */
+ int baseline; /* Offset of baseline from y. */
+ Display *display; /* Display to use for drawing. */
+ Drawable dst; /* Pixmap or window in which to draw */
+ int screenY; /* Y-coordinate in text window that
+ * corresponds to y. */
+{
+ GtkTextLineSegment *ewPtr = (GtkTextLineSegment *) chunkPtr->clientData;
+ int lineX, windowX, windowY, width, height;
+ Tk_Window tkwin;
+
+ tkwin = ewPtr->body.ew.tkwin;
+ if (tkwin == NULL) {
+ return;
+ }
+ if ((x + chunkPtr->width) <= 0) {
+ /*
+ * The window is off-screen; just unmap it.
+ */
+
+ if (ewPtr->body.ew.tkxt->tkwin != Tk_Parent(tkwin)) {
+ Tk_UnmaintainGeometry(tkwin, ewPtr->body.ew.tkxt->tkwin);
+ } else {
+ Tk_UnmapWindow(tkwin);
+ }
+ return;
+ }
+
+ /*
+ * Compute the window's location and size in the text widget, taking
+ * into account the align and stretch values for the window.
+ */
+
+ EmbWinBboxFunc(chunkPtr, 0, screenY, lineHeight, baseline, &lineX,
+ &windowY, &width, &height);
+ windowX = lineX - chunkPtr->x + x;
+
+ if (ewPtr->body.ew.tkxt->tkwin == Tk_Parent(tkwin)) {
+ if ((windowX != Tk_X(tkwin)) || (windowY != Tk_Y(tkwin))
+ || (Tk_ReqWidth(tkwin) != Tk_Width(tkwin))
+ || (height != Tk_Height(tkwin))) {
+ Tk_MoveResizeWindow(tkwin, windowX, windowY, width, height);
+ }
+ Tk_MapWindow(tkwin);
+ } else {
+ Tk_MaintainGeometry(tkwin, ewPtr->body.ew.tkxt->tkwin,
+ windowX, windowY, width, height);
+ }
+
+ /*
+ * Mark the window as displayed so that it won't get unmapped.
+ */
+
+ ewPtr->body.ew.displayed = 1;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EmbWinUndisplayFunc --
+ *
+ * This procedure is called when the chunk for an embedded
+ * window is no longer going to be displayed. It arranges
+ * for the window associated with the chunk to be unmapped.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window is scheduled for unmapping.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+EmbWinUndisplayFunc(tkxt, chunkPtr)
+ GtkTextView *tkxt; /* Overall information about text
+ * widget. */
+ GtkTextDisplayChunk *chunkPtr; /* Chunk that is about to be freed. */
+{
+ GtkTextLineSegment *ewPtr = (GtkTextLineSegment *) chunkPtr->clientData;
+
+ ewPtr->body.ew.chunkCount--;
+ if (ewPtr->body.ew.chunkCount == 0) {
+ /*
+ * Don't unmap the window immediately, since there's a good chance
+ * that it will immediately be redisplayed, perhaps even in the
+ * same place. Instead, schedule the window to be unmapped later;
+ * the call to EmbWinDelayedUnmap will be cancelled in the likely
+ * event that the unmap becomes unnecessary.
+ */
+
+ ewPtr->body.ew.displayed = 0;
+ Tcl_DoWhenIdle(EmbWinDelayedUnmap, (gpointer) ewPtr);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EmbWinBboxFunc --
+ *
+ * This procedure is called to compute the bounding box of
+ * the area occupied by an embedded window.
+ *
+ * Results:
+ * There is no return value. *xPtr and *yPtr are filled in
+ * with the coordinates of the upper left corner of the
+ * window, and *widthPtr and *heightPtr are filled in with
+ * the dimensions of the window in pixels. Note: not all
+ * of the returned bbox is necessarily visible on the screen
+ * (the rightmost part might be off-screen to the right,
+ * and the bottommost part might be off-screen to the bottom).
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+EmbWinBboxFunc(chunkPtr, index, y, lineHeight, baseline, xPtr, yPtr,
+ widthPtr, heightPtr)
+ GtkTextDisplayChunk *chunkPtr; /* Chunk containing desired char. */
+ int index; /* Index of desired character within
+ * the chunk. */
+ int y; /* Topmost pixel in area allocated
+ * for this line. */
+ int lineHeight; /* Total height of line. */
+ int baseline; /* Location of line's baseline, in
+ * pixels measured down from y. */
+ int *xPtr, *yPtr; /* Gets filled in with coords of
+ * character's upper-left pixel. */
+ int *widthPtr; /* Gets filled in with width of
+ * character, in pixels. */
+ int *heightPtr; /* Gets filled in with height of
+ * character, in pixels. */
+{
+ GtkTextLineSegment *ewPtr = (GtkTextLineSegment *) chunkPtr->clientData;
+ Tk_Window tkwin;
+
+ tkwin = ewPtr->body.ew.tkwin;
+ if (tkwin != NULL) {
+ *widthPtr = Tk_ReqWidth(tkwin);
+ *heightPtr = Tk_ReqHeight(tkwin);
+ } else {
+ *widthPtr = 0;
+ *heightPtr = 0;
+ }
+ *xPtr = chunkPtr->x + ewPtr->body.ew.padX;
+ if (ewPtr->body.ew.stretch) {
+ if (ewPtr->body.ew.align == ALIGN_BASELINE) {
+ *heightPtr = baseline - ewPtr->body.ew.padY;
+ } else {
+ *heightPtr = lineHeight - 2*ewPtr->body.ew.padY;
+ }
+ }
+ switch (ewPtr->body.ew.align) {
+ case ALIGN_BOTTOM:
+ *yPtr = y + (lineHeight - *heightPtr - ewPtr->body.ew.padY);
+ break;
+ case ALIGN_CENTER:
+ *yPtr = y + (lineHeight - *heightPtr)/2;
+ break;
+ case ALIGN_TOP:
+ *yPtr = y + ewPtr->body.ew.padY;
+ break;
+ case ALIGN_BASELINE:
+ *yPtr = y + (baseline - *heightPtr);
+ break;
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * EmbWinDelayedUnmap --
+ *
+ * This procedure is an idle handler that does the actual
+ * work of unmapping an embedded window. See the comment
+ * in EmbWinUndisplayFunc for details.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The window gets unmapped, unless its chunk reference count
+ * has become non-zero again.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+EmbWinDelayedUnmap(clientData)
+ gpointer clientData; /* Token for the window to
+ * be unmapped. */
+{
+ GtkTextLineSegment *ewPtr = (GtkTextLineSegment *) clientData;
+
+ if (!ewPtr->body.ew.displayed && (ewPtr->body.ew.tkwin != NULL)) {
+ if (ewPtr->body.ew.tkxt->tkwin != Tk_Parent(ewPtr->body.ew.tkwin)) {
+ Tk_UnmaintainGeometry(ewPtr->body.ew.tkwin,
+ ewPtr->body.ew.tkxt->tkwin);
+ } else {
+ Tk_UnmapWindow(ewPtr->body.ew.tkwin);
+ }
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * GtkTextViewWindowIndex --
+ *
+ * Given the name of an embedded window within a text widget,
+ * returns an index corresponding to the window's position
+ * in the text.
+ *
+ * Results:
+ * The return value is 1 if there is an embedded window by
+ * the given name in the text widget, 0 otherwise. If the
+ * window exists, *indexPtr is filled in with its index.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+int
+GtkTextViewWindowIndex(tkxt, name, indexPtr)
+ GtkTextView *tkxt; /* Text widget containing window. */
+ char *name; /* Name of window. */
+ GtkTextIndex *indexPtr; /* Index information gets stored here. */
+{
+ Tcl_HashEntry *hPtr;
+ GtkTextLineSegment *ewPtr;
+
+ hPtr = Tcl_FindHashEntry(&tkxt->windowTable, name);
+ if (hPtr == NULL) {
+ return 0;
+ }
+ ewPtr = (GtkTextLineSegment *) Tcl_GetHashValue(hPtr);
+ indexPtr->tree = tkxt->tree;
+ indexPtr->line = ewPtr->body.ew.line;
+ indexPtr->byteIndex = gtk_text_line_segment_get_offset(ewPtr, indexPtr->line);
+ return 1;
+}
+#endif
diff --git a/gtk/gtktextchild.h b/gtk/gtktextchild.h
new file mode 100644
index 000000000..9ce8fd5fc
--- /dev/null
+++ b/gtk/gtktextchild.h
@@ -0,0 +1,24 @@
+#ifndef GTK_TEXT_CHILD_H
+#define GTK_TEXT_CHILD_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <gtk/gtktexttypes.h>
+
+typedef struct _GtkTextPixmap GtkTextPixmap;
+
+struct _GtkTextPixmap {
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+};
+
+GtkTextLineSegment *gtk_text_pixmap_segment_new(GdkPixmap *pixmap, GdkBitmap *mask);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/gtk/gtktextdisplay.c b/gtk/gtktextdisplay.c
new file mode 100644
index 000000000..a6c1ac261
--- /dev/null
+++ b/gtk/gtktextdisplay.c
@@ -0,0 +1,609 @@
+/* gtktextdisplay.c - display layed-out text
+ *
+ * Copyright (c) 1992-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ * Copyright (c) 2000 Red Hat, Inc.
+ * Tk->Gtk port by Havoc Pennington
+ *
+ *
+ * This software is copyrighted by the Regents of the University of
+ * California, Sun Microsystems, Inc., and other parties. The
+ * following terms apply to all files associated with the software
+ * unless explicitly disclaimed in individual files.
+ *
+ * The authors hereby grant permission to use, copy, modify,
+ * distribute, and license this software and its documentation for any
+ * purpose, provided that existing copyright notices are retained in
+ * all copies and that this notice is included verbatim in any
+ * distributions. No written agreement, license, or royalty fee is
+ * required for any of the authorized uses. Modifications to this
+ * software may be copyrighted by their authors and need not follow
+ * the licensing terms described here, provided that the new terms are
+ * clearly indicated on the first page of each file where they apply.
+ *
+ * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+ * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
+ * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
+ * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
+ * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
+ * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * GOVERNMENT USE: If you are acquiring this software on behalf of the
+ * U.S. government, the Government shall have only "Restricted Rights"
+ * in the software and related documentation as defined in the Federal
+ * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
+ * are acquiring the software on behalf of the Department of Defense,
+ * the software shall be classified as "Commercial Computer Software"
+ * and the Government shall have only "Restricted Rights" as defined
+ * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
+ * foregoing, the authors grant the U.S. Government and others acting
+ * in its behalf permission to use and distribute the software in
+ * accordance with the terms specified in this license.
+ *
+ */
+
+#include "gtktextdisplay.h"
+#include "gtktextiterprivate.h"
+#include <pango/pangox.h>
+#include "x11/gdkx.h"
+
+typedef struct _GtkTextRenderState GtkTextRenderState;
+
+struct _GtkTextRenderState
+{
+ GtkWidget *widget;
+
+ GtkTextAppearance *last_appearance;
+ GdkGC *fg_gc;
+ GdkGC *bg_gc;
+ GdkRectangle clip_rect;
+};
+
+static void get_item_properties (PangoItem *item,
+ GtkTextAppearance **appearance);
+static GdkRegion *get_selected_clip (GtkTextRenderState *render_state,
+ PangoLayout *layout,
+ PangoLayoutLine *line,
+ int x,
+ int y,
+ int height,
+ int start_index,
+ int end_index);
+
+static GtkTextRenderState *
+gtk_text_render_state_new (GtkWidget *widget,
+ GdkDrawable *drawable,
+ GdkRectangle *clip_rect)
+{
+ GtkTextRenderState *state = g_new0 (GtkTextRenderState, 1);
+
+ state->widget = widget;
+ state->fg_gc = gdk_gc_new (drawable);
+ state->bg_gc = gdk_gc_new (drawable);
+ state->clip_rect = *clip_rect;
+
+ return state;
+}
+
+static void
+gtk_text_render_state_destroy (GtkTextRenderState *state)
+{
+ gdk_gc_unref (state->fg_gc);
+ gdk_gc_unref (state->bg_gc);
+
+ g_free (state);
+}
+
+static void
+gtk_text_render_state_set_color (GtkTextRenderState *state,
+ GdkGC *gc,
+ GdkColor *color)
+{
+ gdk_colormap_alloc_color (gtk_widget_get_colormap (state->widget), color, FALSE, TRUE);
+ gdk_gc_set_foreground (gc, color);
+}
+
+static void
+gtk_text_render_state_update (GtkTextRenderState *state,
+ GtkTextAppearance *new_appearance)
+{
+ if (!state->last_appearance ||
+ !gdk_color_equal (&new_appearance->fg_color, &state->last_appearance->fg_color))
+ gtk_text_render_state_set_color (state, state->fg_gc, &new_appearance->fg_color);
+
+ if (!state->last_appearance ||
+ new_appearance->fg_stipple != state->last_appearance->fg_stipple)
+ {
+ if (new_appearance->fg_stipple)
+ {
+ gdk_gc_set_fill(state->fg_gc, GDK_STIPPLED);
+ gdk_gc_set_stipple(state->fg_gc, new_appearance->fg_stipple);
+ }
+ else
+ {
+ gdk_gc_set_fill(state->fg_gc, GDK_SOLID);
+ }
+ }
+
+ if (new_appearance->draw_bg)
+ {
+ if (!state->last_appearance ||
+ !gdk_color_equal (&new_appearance->bg_color, &state->last_appearance->bg_color))
+ gtk_text_render_state_set_color (state, state->bg_gc, &new_appearance->bg_color);
+
+ if (!state->last_appearance ||
+ new_appearance->bg_stipple != state->last_appearance->bg_stipple)
+ {
+ if (new_appearance->bg_stipple)
+ {
+ gdk_gc_set_fill(state->bg_gc, GDK_STIPPLED);
+ gdk_gc_set_stipple(state->bg_gc, new_appearance->bg_stipple);
+ }
+ else
+ {
+ gdk_gc_set_fill(state->bg_gc, GDK_SOLID);
+ }
+ }
+ }
+
+ state->last_appearance = new_appearance;
+}
+
+static void
+render_layout_line (GdkDrawable *drawable,
+ GtkTextRenderState *render_state,
+ PangoLayoutLine *line,
+ int x,
+ int y,
+ gboolean selected)
+{
+ GSList *tmp_list = line->runs;
+ PangoRectangle overall_rect;
+ PangoRectangle logical_rect;
+ PangoRectangle ink_rect;
+
+ gint x_off = 0;
+
+ pango_layout_line_get_extents (line, NULL, &overall_rect);
+
+ while (tmp_list)
+ {
+ PangoLayoutRun *run = tmp_list->data;
+ GtkTextAppearance *appearance;
+
+ tmp_list = tmp_list->next;
+
+ get_item_properties (run->item, &appearance);
+
+ if (appearance) /* A text segment */
+ {
+ GdkGC *fg_gc;
+
+ if (selected)
+ {
+ fg_gc = render_state->widget->style->fg_gc[GTK_STATE_SELECTED];
+ }
+ else
+ {
+ gtk_text_render_state_update (render_state, appearance);
+
+ fg_gc = render_state->fg_gc;
+ }
+
+ if (appearance->underline == PANGO_UNDERLINE_NONE && !appearance->overstrike)
+ pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
+ NULL, &logical_rect);
+ else
+ pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
+ &ink_rect, &logical_rect);
+
+ if (appearance->draw_bg && !selected)
+ gdk_draw_rectangle (drawable, render_state->bg_gc, TRUE,
+ x + (x_off + logical_rect.x) / PANGO_SCALE,
+ y + logical_rect.y / PANGO_SCALE,
+ logical_rect.width / PANGO_SCALE,
+ logical_rect.height / PANGO_SCALE);
+
+ gdk_draw_glyphs (drawable, fg_gc,
+ run->item->analysis.font,
+ x + x_off / PANGO_SCALE, y, run->glyphs);
+
+ switch (appearance->underline)
+ {
+ case PANGO_UNDERLINE_NONE:
+ break;
+ case PANGO_UNDERLINE_DOUBLE:
+ gdk_draw_line (drawable, fg_gc,
+ x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + 4,
+ x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + 4);
+ /* Fall through */
+ case PANGO_UNDERLINE_SINGLE:
+ gdk_draw_line (drawable, fg_gc,
+ x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + 2,
+ x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + 2);
+ break;
+ case PANGO_UNDERLINE_LOW:
+ gdk_draw_line (drawable, fg_gc,
+ x + (x_off + ink_rect.x) / PANGO_SCALE - 1, y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 2,
+ x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, y + (ink_rect.y + ink_rect.height) / PANGO_SCALE + 2);
+ break;
+ }
+
+ if (appearance->overstrike)
+ {
+ gint overstrike_y = y + (0.3 * logical_rect.y) / PANGO_SCALE;
+ gdk_draw_line (drawable, fg_gc,
+ x + (x_off + ink_rect.x) / PANGO_SCALE - 1, overstrike_y,
+ x + (x_off + ink_rect.x + ink_rect.width) / PANGO_SCALE, overstrike_y);
+ }
+
+ x_off += logical_rect.width;
+ }
+ }
+}
+
+static void
+render_para (GdkDrawable *drawable,
+ GtkTextRenderState *render_state,
+ GtkTextLineDisplay *line_display,
+ int x,
+ int y,
+ int selection_start_index,
+ int selection_end_index)
+{
+ PangoRectangle logical_rect;
+ GSList *tmp_list;
+ PangoAlignment align;
+ PangoLayout *layout = line_display->layout;
+ int indent;
+ int total_width;
+ int y_offset = 0;
+ int byte_offset = 0;
+
+ gboolean first = TRUE;
+
+ indent = pango_layout_get_indent (layout);
+ total_width = pango_layout_get_width (layout);
+ align = pango_layout_get_alignment (layout);
+
+ if (total_width < 0)
+ total_width = line_display->total_width * PANGO_SCALE;
+
+ tmp_list = pango_layout_get_lines (layout);
+ while (tmp_list)
+ {
+ PangoLayoutLine *line = tmp_list->data;
+ int x_offset;
+ int selection_y, selection_height;
+
+ pango_layout_line_get_extents (line, NULL, &logical_rect);
+
+ x_offset = line_display->left_margin * PANGO_SCALE;
+
+ switch (align)
+ {
+ case PANGO_ALIGN_RIGHT:
+ x_offset += total_width - logical_rect.width;
+ break;
+ case PANGO_ALIGN_CENTER:
+ x_offset += (total_width - logical_rect.width) / 2;
+ break;
+ default:
+ break;
+ }
+
+ if (first)
+ {
+ if (indent > 0)
+ {
+ if (align == PANGO_ALIGN_LEFT)
+ x_offset += indent;
+ else
+ x_offset -= indent;
+ }
+ }
+ else
+ {
+ if (indent < 0)
+ {
+ if (align == PANGO_ALIGN_LEFT)
+ x_offset -= indent;
+ else
+ x_offset += indent;
+ }
+ }
+
+ selection_y = y + y_offset / PANGO_SCALE;
+ selection_height = logical_rect.height / PANGO_SCALE;
+
+ if (first)
+ {
+ selection_y -= line_display->top_margin;
+ selection_height += line_display->top_margin;
+ }
+ if (!tmp_list->next)
+ selection_height += line_display->bottom_margin;
+
+ first = FALSE;
+
+ if (selection_start_index < byte_offset &&
+ selection_end_index > line->length + byte_offset) /* All selected */
+ {
+ gdk_draw_rectangle (drawable,
+ render_state->widget->style->bg_gc[GTK_STATE_SELECTED],
+ TRUE,
+ x + line_display->left_margin, selection_y,
+ total_width / PANGO_SCALE, selection_height);
+ render_layout_line (drawable, render_state,
+ line, x + x_offset / PANGO_SCALE, y + (y_offset - logical_rect.y) / PANGO_SCALE,
+ TRUE);
+ }
+ else
+ {
+ render_layout_line (drawable, render_state,
+ line, x + x_offset / PANGO_SCALE, y + (y_offset - logical_rect.y) / PANGO_SCALE,
+ FALSE);
+
+ if (selection_start_index < byte_offset + line->length &&
+ selection_end_index > byte_offset) /* Some selected */
+ {
+ GdkRegion *clip_region = get_selected_clip (render_state, layout, line,
+ x + line_display->x_offset,
+ selection_y, selection_height,
+ selection_start_index, selection_end_index);
+
+ gdk_gc_set_clip_region (render_state->widget->style->fg_gc [GTK_STATE_SELECTED], clip_region);
+ gdk_gc_set_clip_region (render_state->widget->style->bg_gc [GTK_STATE_SELECTED], clip_region);
+
+ gdk_draw_rectangle (drawable,
+ render_state->widget->style->bg_gc[GTK_STATE_SELECTED],
+ TRUE,
+ x + x_offset / PANGO_SCALE, selection_y,
+ logical_rect.width / PANGO_SCALE,
+ selection_height);
+
+ render_layout_line (drawable, render_state,
+ line, x + x_offset / PANGO_SCALE, y + (y_offset - logical_rect.y) / PANGO_SCALE,
+ TRUE);
+
+ gdk_gc_set_clip_region (render_state->widget->style->fg_gc [GTK_STATE_SELECTED], NULL);
+ gdk_gc_set_clip_region (render_state->widget->style->bg_gc [GTK_STATE_SELECTED], NULL);
+
+ gdk_region_destroy (clip_region);
+
+ /* Paint in the ends of the line */
+ if (x_offset > line_display->left_margin * PANGO_SCALE &&
+ ((line_display->direction == GTK_TEXT_DIR_LTR && selection_start_index < byte_offset) ||
+ (line_display->direction == GTK_TEXT_DIR_RTL && selection_end_index > byte_offset + line->length)))
+ {
+ gdk_draw_rectangle (drawable,
+ render_state->widget->style->bg_gc[GTK_STATE_SELECTED],
+ TRUE,
+ x + line_display->left_margin, selection_y,
+ x + x_offset / PANGO_SCALE - line_display->left_margin,
+ selection_height);
+ }
+
+ if (x_offset + logical_rect.width < line_display->left_margin * PANGO_SCALE + total_width &&
+ ((line_display->direction == GTK_TEXT_DIR_LTR && selection_end_index > byte_offset + line->length) ||
+ (line_display->direction == GTK_TEXT_DIR_RTL && selection_start_index < byte_offset)))
+ {
+
+
+ gdk_draw_rectangle (drawable,
+ render_state->widget->style->bg_gc[GTK_STATE_SELECTED],
+ TRUE,
+ x + (x_offset + logical_rect.width) / PANGO_SCALE,
+ selection_y,
+ x + (line_display->left_margin * PANGO_SCALE + total_width - x_offset - logical_rect.width) / PANGO_SCALE,
+ selection_height);
+ }
+ }
+ }
+
+ byte_offset += line->length;
+ y_offset += logical_rect.height;
+ tmp_list = tmp_list->next;
+ }
+}
+
+static GdkRegion *
+get_selected_clip (GtkTextRenderState *render_state,
+ PangoLayout *layout,
+ PangoLayoutLine *line,
+ int x,
+ int y,
+ int height,
+ int start_index,
+ int end_index)
+{
+ gint *ranges;
+ gint n_ranges, i;
+ GdkRegion *clip_region = gdk_region_new ();
+ GdkRegion *tmp_region;
+ PangoRectangle logical_rect;
+
+ pango_layout_line_get_extents (line, NULL, &logical_rect);
+ pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges);
+
+ for (i=0; i < n_ranges; i++)
+ {
+ GdkRectangle rect;
+
+ rect.x = x + ranges[2*i] / PANGO_SCALE;
+ rect.y = y;
+ rect.width = (ranges[2*i + 1] - ranges[2*i]) / PANGO_SCALE;
+ rect.height = height;
+
+ gdk_region_union_with_rect (clip_region, &rect);
+ }
+
+ tmp_region = gdk_region_rectangle (&render_state->clip_rect);
+ gdk_region_intersect (clip_region, tmp_region);
+ gdk_region_destroy (tmp_region);
+
+ g_free (ranges);
+ return clip_region;
+}
+
+static void
+get_item_properties (PangoItem *item,
+ GtkTextAppearance **appearance)
+{
+ GSList *tmp_list = item->extra_attrs;
+
+ *appearance = NULL;
+
+ while (tmp_list)
+ {
+ PangoAttribute *attr = tmp_list->data;
+
+ if (attr->klass->type == gtk_text_attr_appearance_type)
+ {
+ *appearance = &((GtkTextAttrAppearance *)attr)->appearance;
+ return;
+ }
+ }
+}
+
+void
+gtk_text_layout_draw (GtkTextLayout *layout,
+ GtkWidget *widget,
+ GdkDrawable *drawable,
+ /* Location of the layout
+ in buffer coordinates */
+ gint x_offset,
+ gint y_offset,
+ /* Region of the layout to
+ render */
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ GdkRectangle clip;
+ gint current_y;
+ GSList *line_list;
+ GSList *tmp_list;
+ GSList *cursor_list;
+ GtkTextRenderState *render_state;
+ GtkTextIter selection_start, selection_end;
+ gboolean have_selection = FALSE;
+
+ g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+ g_return_if_fail (layout->default_style != NULL);
+ g_return_if_fail (layout->buffer != NULL);
+ g_return_if_fail (drawable != NULL);
+ g_return_if_fail (x_offset >= 0);
+ g_return_if_fail (y_offset >= 0);
+ g_return_if_fail (width >= 0);
+ g_return_if_fail (height >= 0);
+
+ if (width == 0 || height == 0)
+ return;
+
+ line_list = gtk_text_layout_get_lines (layout, y + y_offset, y + y_offset + height, &current_y);
+ current_y -= y_offset;
+
+ if (line_list == NULL)
+ return; /* nothing on the screen */
+
+ clip.x = x;
+ clip.y = y;
+ clip.width = width;
+ clip.height = height;
+
+ render_state = gtk_text_render_state_new (widget, drawable, &clip);
+
+ gdk_gc_set_clip_rectangle (render_state->fg_gc, &clip);
+ gdk_gc_set_clip_rectangle (render_state->bg_gc, &clip);
+
+ gtk_text_layout_wrap_loop_start (layout);
+
+ if (gtk_text_buffer_get_selection_bounds (layout->buffer,
+ &selection_start,
+ &selection_end))
+ have_selection = TRUE;
+
+ tmp_list = line_list;
+ while (tmp_list != NULL)
+ {
+ GtkTextLineDisplay *line_display;
+ gint selection_start_index = -1;
+ gint selection_end_index = -1;
+
+ GtkTextLine *line = tmp_list->data;
+
+ line_display = gtk_text_layout_get_line_display (layout, line, FALSE);
+
+ if (have_selection)
+ {
+ GtkTextIter line_start, line_end;
+ gint byte_count = gtk_text_line_byte_count (line);
+
+ gtk_text_btree_get_iter_at_line (layout->buffer->tree, &line_start,
+ line, 0);
+ gtk_text_btree_get_iter_at_line (layout->buffer->tree, &line_end,
+ line, byte_count - 1);
+
+ if (gtk_text_iter_compare (&selection_start, &line_end) < 0 &&
+ gtk_text_iter_compare (&selection_end, &line_start) > 0)
+ {
+ if (gtk_text_iter_compare (&selection_start, &line_start) >= 0)
+ selection_start_index = gtk_text_iter_get_line_byte (&selection_start);
+ else
+ selection_start_index = -1;
+
+ if (gtk_text_iter_compare (&selection_end, &line_end) <= 0)
+ selection_end_index = gtk_text_iter_get_line_byte (&selection_end);
+ else
+ selection_end_index = byte_count;
+ }
+ }
+
+ render_para (drawable, render_state, line_display,
+ - x_offset,
+ current_y + line_display->top_margin,
+ selection_start_index, selection_end_index);
+
+
+ /* We paint the cursors last, because they overlap another chunk
+ and need to appear on top. */
+
+ cursor_list = line_display->cursors;
+ while (cursor_list)
+ {
+ GtkTextCursorDisplay *cursor = cursor_list->data;
+ GdkGC *gc;
+
+ if (cursor->is_strong)
+ gc = widget->style->bg_gc[GTK_STATE_SELECTED];
+ else
+ gc = widget->style->fg_gc[GTK_STATE_NORMAL];
+
+ gdk_draw_line (drawable, gc,
+ line_display->x_offset + cursor->x,
+ current_y + line_display->top_margin + cursor->y,
+ line_display->x_offset + cursor->x,
+ current_y + line_display->top_margin + cursor->y + cursor->height);
+
+ cursor_list = cursor_list->next;
+ }
+
+ current_y += line_display->height;
+ gtk_text_layout_free_line_display (layout, line_display);
+
+ tmp_list = g_slist_next (tmp_list);
+ }
+
+ gtk_text_layout_wrap_loop_end (layout);
+ gtk_text_render_state_destroy (render_state);
+
+ g_slist_free (line_list);
+}
diff --git a/gtk/gtktextdisplay.h b/gtk/gtktextdisplay.h
new file mode 100644
index 000000000..8f43b6da0
--- /dev/null
+++ b/gtk/gtktextdisplay.h
@@ -0,0 +1,39 @@
+#ifndef GTK_TEXT_DISPLAY_H
+#define GTK_TEXT_DISPLAY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ A semi-public header intended for use by code that also
+ uses GtkTextLayout
+*/
+
+#include <gtk/gtktextlayout.h>
+
+/* The drawable should be pre-initialized to your preferred
+ background. */
+void gtk_text_layout_draw (GtkTextLayout *layout,
+ /* Widget to grab some style info from */
+ GtkWidget *widget,
+ /* Drawable to render to */
+ GdkDrawable *drawable,
+ /* Position of the drawable
+ in layout coordinates */
+ gint x_offset,
+ gint y_offset,
+ /* Region of the layout to
+ render. x,y must be inside
+ the drawable. */
+ gint x,
+ gint y,
+ gint width,
+ gint height);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/gtk/gtktextiter.c b/gtk/gtktextiter.c
new file mode 100644
index 000000000..41a16916c
--- /dev/null
+++ b/gtk/gtktextiter.c
@@ -0,0 +1,2384 @@
+#include "gtktextiter.h"
+#include "gtktextbtree.h"
+#include "gtktextiterprivate.h"
+#include "gtkdebug.h"
+#include <ctype.h>
+
+typedef struct _GtkTextRealIter GtkTextRealIter;
+
+struct _GtkTextRealIter {
+ /* Always-valid information */
+ GtkTextBTree *tree;
+ GtkTextLine *line;
+ /* At least one of these is always valid;
+ if invalid, they are -1.
+
+ If the line byte offset is valid, so is the segment byte offset;
+ and ditto for char offsets. */
+ gint line_byte_offset;
+ gint line_char_offset;
+ /* These two are valid if >= 0 */
+ gint cached_char_index;
+ gint cached_line_number;
+ /* Stamps to detect the buffer changing under us */
+ gint chars_changed_stamp;
+ gint segments_changed_stamp;
+ /* Valid if the segments_changed_stamp is up-to-date */
+ GtkTextLineSegment *segment; /* indexable segment we index */
+ GtkTextLineSegment *any_segment; /* first segment in our location,
+ maybe same as "segment" */
+ /* One of these will always be valid if segments_changed_stamp is
+ up-to-date. If invalid, they are -1.
+
+ If the line byte offset is valid, so is the segment byte offset;
+ and ditto for char offsets. */
+ gint segment_byte_offset;
+ gint segment_char_offset;
+ /* These are here for binary-compatible expansion space. */
+ gpointer pad1;
+ gint pad2;
+};
+
+/* These "set" functions should not assume any fields
+ other than the char stamp and the tree are valid.
+*/
+static void
+iter_set_common(GtkTextRealIter *iter,
+ GtkTextLine *line)
+{
+ /* Update segments stamp */
+ iter->segments_changed_stamp =
+ gtk_text_btree_get_segments_changed_stamp(iter->tree);
+
+ iter->line = line;
+
+ iter->line_byte_offset = -1;
+ iter->line_char_offset = -1;
+ iter->segment_byte_offset = -1;
+ iter->segment_char_offset = -1;
+ iter->cached_char_index = -1;
+ iter->cached_line_number = -1;
+}
+
+static void
+iter_set_from_byte_offset(GtkTextRealIter *iter,
+ GtkTextLine *line,
+ gint byte_offset)
+{
+ iter_set_common(iter, line);
+
+ gtk_text_line_byte_locate(iter->line,
+ byte_offset,
+ &iter->segment,
+ &iter->any_segment,
+ &iter->segment_byte_offset,
+ &iter->line_byte_offset);
+
+}
+
+static void
+iter_set_from_char_offset(GtkTextRealIter *iter,
+ GtkTextLine *line,
+ gint char_offset)
+{
+ iter_set_common(iter, line);
+
+ gtk_text_line_char_locate(iter->line,
+ char_offset,
+ &iter->segment,
+ &iter->any_segment,
+ &iter->segment_char_offset,
+ &iter->line_char_offset);
+}
+
+static void
+iter_set_from_segment(GtkTextRealIter *iter,
+ GtkTextLine *line,
+ GtkTextLineSegment *segment)
+{
+ GtkTextLineSegment *seg;
+ gint byte_offset;
+
+ /* This could theoretically be optimized by computing all the iter
+ fields in this same loop, but I'm skipping it for now. */
+ byte_offset = 0;
+ seg = line->segments;
+ while (seg != segment)
+ {
+ byte_offset += seg->byte_count;
+ seg = seg->next;
+ }
+
+ iter_set_from_byte_offset(iter, line, byte_offset);
+}
+
+/* This function ensures that the segment-dependent information is
+ truly computed lazily; often we don't need to do the full make_real
+ work. */
+static GtkTextRealIter*
+gtk_text_iter_make_surreal(const GtkTextIter *_iter)
+{
+ GtkTextRealIter *iter = (GtkTextRealIter*)_iter;
+
+ if (iter->chars_changed_stamp !=
+ gtk_text_btree_get_chars_changed_stamp(iter->tree))
+ {
+ g_warning("Invalid text buffer iterator: either the iterator is uninitialized, or the characters/pixmaps/widgets in the buffer have been modified since the iterator was created.\nYou must use marks, character numbers, or line numbers to preserve a position across buffer modifications.\nYou can apply tags and insert marks without invalidating your iterators, however.");
+ return NULL;
+ }
+
+ /* We don't update the segments information since we are becoming
+ only surreal. However we do invalidate the segments information
+ if appropriate, to be sure we segfault if we try to use it and we
+ should have used make_real. */
+
+ if (iter->segments_changed_stamp !=
+ gtk_text_btree_get_segments_changed_stamp(iter->tree))
+ {
+ iter->segment = NULL;
+ iter->any_segment = NULL;
+ /* set to segfault-causing values. */
+ iter->segment_byte_offset = -10000;
+ iter->segment_char_offset = -10000;
+ }
+
+ return iter;
+}
+
+static GtkTextRealIter*
+gtk_text_iter_make_real(const GtkTextIter *_iter)
+{
+ GtkTextRealIter *iter;
+
+ iter = gtk_text_iter_make_surreal(_iter);
+
+ if (iter->segments_changed_stamp !=
+ gtk_text_btree_get_segments_changed_stamp(iter->tree))
+ {
+ if (iter->line_byte_offset >= 0)
+ {
+ iter_set_from_byte_offset(iter,
+ iter->line,
+ iter->line_byte_offset);
+ }
+ else
+ {
+ g_assert(iter->line_char_offset >= 0);
+
+ iter_set_from_char_offset(iter,
+ iter->line,
+ iter->line_char_offset);
+ }
+ }
+
+ g_assert(iter->segment != NULL);
+ g_assert(iter->any_segment != NULL);
+ g_assert(iter->segment->char_count > 0);
+
+ return iter;
+}
+
+static GtkTextRealIter*
+iter_init_common(GtkTextIter *_iter,
+ GtkTextBTree *tree)
+{
+ GtkTextRealIter *iter = (GtkTextRealIter*)_iter;
+
+ g_return_val_if_fail(iter != NULL, NULL);
+ g_return_val_if_fail(tree != NULL, NULL);
+
+ iter->tree = tree;
+
+ iter->chars_changed_stamp =
+ gtk_text_btree_get_chars_changed_stamp(iter->tree);
+
+ return iter;
+}
+
+static GtkTextRealIter*
+iter_init_from_segment(GtkTextIter *iter,
+ GtkTextBTree *tree,
+ GtkTextLine *line,
+ GtkTextLineSegment *segment)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(line != NULL, NULL);
+
+ real = iter_init_common(iter, tree);
+
+ iter_set_from_segment(real, line, segment);
+
+ return real;
+}
+
+static GtkTextRealIter*
+iter_init_from_byte_offset(GtkTextIter *iter,
+ GtkTextBTree *tree,
+ GtkTextLine *line,
+ gint line_byte_offset)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(line != NULL, NULL);
+
+ real = iter_init_common(iter, tree);
+
+ iter_set_from_byte_offset(real, line, line_byte_offset);
+
+ return real;
+}
+
+static GtkTextRealIter*
+iter_init_from_char_offset(GtkTextIter *iter,
+ GtkTextBTree *tree,
+ GtkTextLine *line,
+ gint line_char_offset)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(line != NULL, NULL);
+
+ real = iter_init_common(iter, tree);
+
+ iter_set_from_char_offset(real, line, line_char_offset);
+
+ return real;
+}
+
+static inline void
+invalidate_segment(GtkTextRealIter *iter)
+{
+ iter->segments_changed_stamp -= 1;
+}
+
+static inline void
+invalidate_char_index(GtkTextRealIter *iter)
+{
+ iter->cached_char_index = -1;
+}
+
+static inline void
+invalidate_line_number(GtkTextRealIter *iter)
+{
+ iter->cached_line_number = -1;
+}
+
+static inline void
+adjust_char_index(GtkTextRealIter *iter, gint count)
+{
+ if (iter->cached_char_index >= 0)
+ iter->cached_char_index += count;
+}
+
+static inline void
+adjust_line_number(GtkTextRealIter *iter, gint count)
+{
+ if (iter->cached_line_number >= 0)
+ iter->cached_line_number += count;
+}
+
+static inline void
+adjust_char_offsets(GtkTextRealIter *iter, gint count)
+{
+ if (iter->line_char_offset >= 0)
+ {
+ iter->line_char_offset += count;
+ g_assert(iter->segment_char_offset >= 0);
+ iter->segment_char_offset += count;
+ }
+}
+
+static inline void
+adjust_byte_offsets(GtkTextRealIter *iter, gint count)
+{
+ if (iter->line_byte_offset >= 0)
+ {
+ iter->line_byte_offset += count;
+ g_assert(iter->segment_byte_offset >= 0);
+ iter->segment_byte_offset += count;
+ }
+}
+
+static inline void
+ensure_char_offsets(GtkTextRealIter *iter)
+{
+ if (iter->line_char_offset < 0)
+ {
+ g_assert(iter->line_byte_offset >= 0);
+
+ gtk_text_line_byte_to_char_offsets(iter->line,
+ iter->line_byte_offset,
+ &iter->line_char_offset,
+ &iter->segment_char_offset);
+ }
+}
+
+static inline void
+ensure_byte_offsets(GtkTextRealIter *iter)
+{
+ if (iter->line_byte_offset < 0)
+ {
+ g_assert(iter->line_char_offset >= 0);
+
+ gtk_text_line_char_to_byte_offsets(iter->line,
+ iter->line_char_offset,
+ &iter->line_byte_offset,
+ &iter->segment_byte_offset);
+ }
+}
+
+#if 1
+static void
+check_invariants(const GtkTextIter *iter)
+{
+ if (gtk_debug_flags & GTK_DEBUG_TEXT)
+ gtk_text_iter_check(iter);
+}
+#else
+#define check_invariants(x)
+#endif
+
+GtkTextBuffer*
+gtk_text_iter_get_buffer(const GtkTextIter *iter)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, NULL);
+
+ real = gtk_text_iter_make_surreal(iter);
+
+ if (real == NULL)
+ return NULL;
+
+ check_invariants(iter);
+
+ return gtk_text_btree_get_buffer(real->tree);
+}
+
+GtkTextIter*
+gtk_text_iter_copy(const GtkTextIter *iter)
+{
+ GtkTextIter *new_iter;
+
+ g_return_val_if_fail(iter != NULL, NULL);
+
+ new_iter = g_new(GtkTextIter, 1);
+
+ *new_iter = *iter;
+
+ return new_iter;
+}
+
+void
+gtk_text_iter_free(GtkTextIter *iter)
+{
+ g_return_if_fail(iter != NULL);
+
+ g_free(iter);
+}
+
+GtkTextLineSegment*
+gtk_text_iter_get_indexable_segment(const GtkTextIter *iter)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, 0);
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return NULL;
+
+ check_invariants(iter);
+
+ g_assert(real->segment != NULL);
+
+ return real->segment;
+}
+
+GtkTextLineSegment*
+gtk_text_iter_get_any_segment(const GtkTextIter *iter)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, 0);
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return NULL;
+
+ check_invariants(iter);
+
+ g_assert(real->any_segment != NULL);
+
+ return real->any_segment;
+}
+
+gint
+gtk_text_iter_get_segment_byte(const GtkTextIter *iter)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, 0);
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return 0;
+
+ ensure_byte_offsets(real);
+
+ check_invariants(iter);
+
+ return real->segment_byte_offset;
+}
+
+gint
+gtk_text_iter_get_segment_char(const GtkTextIter *iter)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, 0);
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return 0;
+
+ ensure_char_offsets(real);
+
+ check_invariants(iter);
+
+ return real->segment_char_offset;
+}
+
+/* This function does not require a still-valid
+ iterator */
+GtkTextLine*
+gtk_text_iter_get_line(const GtkTextIter *iter)
+{
+ const GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, 0);
+
+ real = (const GtkTextRealIter*)iter;
+
+ return real->line;
+}
+
+/* This function does not require a still-valid
+ iterator */
+GtkTextBTree*
+gtk_text_iter_get_btree(const GtkTextIter *iter)
+{
+ const GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, 0);
+
+ real = (const GtkTextRealIter*)iter;
+
+ return real->tree;
+}
+
+/*
+ * Conversions
+ */
+
+gint
+gtk_text_iter_get_char_index(const GtkTextIter *iter)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, 0);
+
+ real = gtk_text_iter_make_surreal(iter);
+
+ if (real == NULL)
+ return 0;
+
+ if (real->cached_char_index < 0)
+ {
+ real->cached_char_index =
+ gtk_text_line_char_index(real->line);
+ ensure_char_offsets(real);
+ real->cached_char_index += real->line_char_offset;
+ }
+
+ check_invariants(iter);
+
+ return real->cached_char_index;
+}
+
+gint
+gtk_text_iter_get_line_number(const GtkTextIter *iter)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, 0);
+
+ real = gtk_text_iter_make_surreal(iter);
+
+ if (real == NULL)
+ return 0;
+
+ if (real->cached_line_number < 0)
+ real->cached_line_number =
+ gtk_text_line_get_number(real->line);
+
+ check_invariants(iter);
+
+ return real->cached_line_number;
+}
+
+gint
+gtk_text_iter_get_line_char(const GtkTextIter *iter)
+{
+
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, 0);
+
+ real = gtk_text_iter_make_surreal(iter);
+
+ if (real == NULL)
+ return 0;
+
+ ensure_char_offsets(real);
+
+ check_invariants(iter);
+
+ return real->line_char_offset;
+}
+
+gint
+gtk_text_iter_get_line_byte(const GtkTextIter *iter)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, 0);
+
+ real = gtk_text_iter_make_surreal(iter);
+
+ if (real == NULL)
+ return 0;
+
+ ensure_byte_offsets(real);
+
+ check_invariants(iter);
+
+ return real->line_byte_offset;
+}
+
+/*
+ * Dereferencing
+ */
+
+gint
+gtk_text_iter_get_char(const GtkTextIter *iter)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, 0);
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return 0;
+
+ check_invariants(iter);
+
+ /* FIXME probably want to special-case the end iterator
+ and either have an error or return 0 */
+
+ if (real->segment->type == &gtk_text_char_type)
+ {
+ GtkTextUniChar ch;
+
+ ensure_byte_offsets(real);
+
+ gtk_text_utf_to_unichar(real->segment->body.chars +
+ real->segment_byte_offset,
+ &ch);
+
+ return ch;
+ }
+ else
+ {
+ /* Unicode "unknown character" 0xFFFD */
+ return gtk_text_unknown_char;
+ }
+}
+
+gchar*
+gtk_text_iter_get_slice (const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ g_return_val_if_fail(start != NULL, NULL);
+ g_return_val_if_fail(end != NULL, NULL);
+
+ check_invariants(start);
+ check_invariants(end);
+
+ return gtk_text_btree_get_text(start, end, TRUE, TRUE);
+}
+
+gchar*
+gtk_text_iter_get_text (const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ g_return_val_if_fail(start != NULL, NULL);
+ g_return_val_if_fail(end != NULL, NULL);
+
+ check_invariants(start);
+ check_invariants(end);
+
+ return gtk_text_btree_get_text(start, end, TRUE, FALSE);
+}
+
+gchar*
+gtk_text_iter_get_visible_slice (const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ g_return_val_if_fail(start != NULL, NULL);
+ g_return_val_if_fail(end != NULL, NULL);
+
+ check_invariants(start);
+ check_invariants(end);
+
+ return gtk_text_btree_get_text(start, end, FALSE, TRUE);
+}
+
+gchar*
+gtk_text_iter_get_visible_text (const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ g_return_val_if_fail(start != NULL, NULL);
+ g_return_val_if_fail(end != NULL, NULL);
+
+ check_invariants(start);
+ check_invariants(end);
+
+ return gtk_text_btree_get_text(start, end, FALSE, FALSE);
+}
+
+gboolean
+gtk_text_iter_get_pixmap (const GtkTextIter *iter,
+ GdkPixmap** pixmap,
+ GdkBitmap** mask)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+ g_return_val_if_fail(pixmap != NULL, FALSE);
+ g_return_val_if_fail(mask != NULL, FALSE);
+
+ *pixmap = NULL;
+ *mask = NULL;
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return FALSE;
+
+ check_invariants(iter);
+
+ if (real->segment->type != &gtk_text_pixmap_type)
+ return FALSE;
+ else
+ {
+ *pixmap = real->segment->body.pixmap.pixmap;
+ *mask = real->segment->body.pixmap.pixmap;
+
+ return TRUE;
+ }
+}
+
+GSList*
+gtk_text_iter_get_toggled_tags (const GtkTextIter *iter,
+ gboolean toggled_on)
+{
+ GtkTextRealIter *real;
+ GtkTextLineSegment *seg;
+ GSList *retval;
+
+ g_return_val_if_fail(iter != NULL, NULL);
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return NULL;
+
+ check_invariants(iter);
+
+ retval = NULL;
+ seg = real->any_segment;
+ while (seg != real->segment)
+ {
+ if (toggled_on)
+ {
+ if (seg->type == &gtk_text_toggle_on_type)
+ {
+ retval = g_slist_prepend(retval, seg->body.toggle.info->tag);
+ }
+ }
+ else
+ {
+ if (seg->type == &gtk_text_toggle_off_type)
+ {
+ retval = g_slist_prepend(retval, seg->body.toggle.info->tag);
+ }
+ }
+
+ seg = seg->next;
+ }
+
+ /* The returned list isn't guaranteed to be in any special order,
+ and it isn't. */
+ return retval;
+}
+
+gboolean
+gtk_text_iter_begins_tag (const GtkTextIter *iter,
+ GtkTextTag *tag)
+{
+ GtkTextRealIter *real;
+ GtkTextLineSegment *seg;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return FALSE;
+
+ check_invariants(iter);
+
+ seg = real->any_segment;
+ while (seg != real->segment)
+ {
+ if (seg->type == &gtk_text_toggle_on_type)
+ {
+ if (tag == NULL ||
+ seg->body.toggle.info->tag == tag)
+ return TRUE;
+ }
+
+ seg = seg->next;
+ }
+
+ return FALSE;
+}
+
+gboolean
+gtk_text_iter_ends_tag (const GtkTextIter *iter,
+ GtkTextTag *tag)
+{
+ GtkTextRealIter *real;
+ GtkTextLineSegment *seg;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return FALSE;
+
+ check_invariants(iter);
+
+ seg = real->any_segment;
+ while (seg != real->segment)
+ {
+ if (seg->type == &gtk_text_toggle_off_type)
+ {
+ if (tag == NULL ||
+ seg->body.toggle.info->tag == tag)
+ return TRUE;
+ }
+
+ seg = seg->next;
+ }
+
+ return FALSE;
+}
+
+gboolean
+gtk_text_iter_toggles_tag (const GtkTextIter *iter,
+ GtkTextTag *tag)
+{
+ GtkTextRealIter *real;
+ GtkTextLineSegment *seg;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return FALSE;
+
+ check_invariants(iter);
+
+ seg = real->any_segment;
+ while (seg != real->segment)
+ {
+ if ( (seg->type == &gtk_text_toggle_off_type ||
+ seg->type == &gtk_text_toggle_on_type) &&
+ (tag == NULL ||
+ seg->body.toggle.info->tag == tag) )
+ return TRUE;
+
+ seg = seg->next;
+ }
+
+ return FALSE;
+}
+
+gboolean
+gtk_text_iter_has_tag (const GtkTextIter *iter,
+ GtkTextTag *tag)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+ g_return_val_if_fail(GTK_IS_TEXT_TAG(tag), FALSE);
+
+ real = gtk_text_iter_make_surreal(iter);
+
+ if (real == NULL)
+ return FALSE;
+
+ check_invariants(iter);
+
+ if (real->line_byte_offset >= 0)
+ {
+ return gtk_text_line_byte_has_tag(real->line, real->tree,
+ real->line_byte_offset, tag);
+ }
+ else
+ {
+ g_assert(real->line_char_offset >= 0);
+ return gtk_text_line_char_has_tag(real->line, real->tree,
+ real->line_char_offset, tag);
+ }
+}
+
+gboolean
+gtk_text_iter_starts_line (const GtkTextIter *iter)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ real = gtk_text_iter_make_surreal(iter);
+
+ if (real == NULL)
+ return FALSE;
+
+ check_invariants(iter);
+
+ if (real->line_byte_offset >= 0)
+ {
+ return (real->line_byte_offset == 0);
+ }
+ else
+ {
+ g_assert(real->line_char_offset >= 0);
+ return (real->line_char_offset == 0);
+ }
+}
+
+gboolean
+gtk_text_iter_ends_line (const GtkTextIter *iter)
+{
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ check_invariants(iter);
+
+ return gtk_text_iter_get_char(iter) == '\n';
+}
+
+gint
+gtk_text_iter_get_chars_in_line (const GtkTextIter *iter)
+{
+ GtkTextRealIter *real;
+ gint count;
+ GtkTextLineSegment *seg;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ real = gtk_text_iter_make_surreal(iter);
+
+ if (real == NULL)
+ return 0;
+
+ check_invariants(iter);
+
+ if (real->line_char_offset >= 0)
+ {
+ /* We can start at the segments we've already found. */
+ count = real->line_char_offset - real->segment_char_offset;
+ seg = gtk_text_iter_get_indexable_segment(iter);
+ }
+ else
+ {
+ /* count whole line. */
+ seg = real->line->segments;
+ count = 0;
+ }
+
+
+ while (seg != NULL)
+ {
+ count += seg->char_count;
+
+ seg = seg->next;
+ }
+
+ return count;
+}
+
+/*
+ * Increments/decrements
+ */
+
+static gboolean
+forward_line_leaving_caches_unmodified(GtkTextRealIter *real)
+{
+ GtkTextLine *new_line;
+
+ new_line = gtk_text_line_next(real->line);
+
+ g_assert(new_line != real->line);
+
+ if (new_line != NULL)
+ {
+ real->line = new_line;
+
+ real->line_byte_offset = 0;
+ real->line_char_offset = 0;
+
+ real->segment_byte_offset = 0;
+ real->segment_char_offset = 0;
+
+ /* Find first segments in new line */
+ real->any_segment = real->line->segments;
+ real->segment = real->any_segment;
+ while (real->segment->char_count == 0)
+ real->segment = real->segment->next;
+
+ return TRUE;
+ }
+ else
+ {
+ /* There is no way to move forward; we were already
+ at the "end" index. (the end index is the last
+ line pointer, segment_byte_offset of 0) */
+
+ g_assert(real->line_char_offset == 0 ||
+ real->line_byte_offset == 0);
+
+ /* The only indexable segment allowed on the bogus
+ line at the end is a single char segment containing
+ a newline. */
+ if (real->segments_changed_stamp ==
+ gtk_text_btree_get_segments_changed_stamp(real->tree))
+ {
+ g_assert(real->segment->type == &gtk_text_char_type);
+ g_assert(real->segment->char_count == 1);
+ }
+ /* We leave real->line as-is */
+
+ return FALSE;
+ }
+}
+
+static gboolean
+forward_char(GtkTextRealIter *real)
+{
+ GtkTextIter *iter = (GtkTextIter*)real;
+
+ check_invariants((GtkTextIter*)real);
+
+ ensure_char_offsets(real);
+
+ if ( (real->segment_char_offset + 1) == real->segment->char_count)
+ {
+ /* Need to move to the next segment; if no next segment,
+ need to move to next line. */
+ return gtk_text_iter_forward_indexable_segment(iter);
+ }
+ else
+ {
+ /* Just moving within a segment. Keep byte count
+ up-to-date, if it was already up-to-date. */
+
+ g_assert(real->segment->type == &gtk_text_char_type);
+
+ if (real->line_byte_offset >= 0)
+ {
+ gint bytes;
+ GtkTextUniChar ch;
+
+ bytes = gtk_text_utf_to_unichar(real->segment->body.chars +
+ real->segment_byte_offset,
+ &ch);
+
+ real->line_byte_offset += bytes;
+ real->segment_byte_offset += bytes;
+
+ g_assert(real->segment_byte_offset < real->segment->byte_count);
+ }
+
+ real->line_char_offset += 1;
+ real->segment_char_offset += 1;
+
+ adjust_char_index(real, 1);
+
+ g_assert(real->segment_char_offset < real->segment->char_count);
+
+ /* We moved into the middle of a segment, so the any_segment
+ must now be the segment we're in the middle of. */
+ real->any_segment = real->segment;
+
+ check_invariants((GtkTextIter*)real);
+
+ return TRUE;
+ }
+}
+
+gboolean
+gtk_text_iter_forward_indexable_segment(GtkTextIter *iter)
+{
+ /* Need to move to the next segment; if no next segment,
+ need to move to next line. */
+ GtkTextLineSegment *seg;
+ GtkTextLineSegment *any_seg;
+ GtkTextRealIter *real;
+ gint chars_skipped;
+ gint bytes_skipped;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return FALSE;
+
+ check_invariants(iter);
+
+ if (real->line_char_offset >= 0)
+ {
+ chars_skipped = real->segment->char_count - real->segment_char_offset;
+ g_assert(chars_skipped > 0);
+ }
+ else
+ chars_skipped = 0;
+
+ if (real->line_byte_offset >= 0)
+ {
+ bytes_skipped = real->segment->byte_count - real->segment_byte_offset;
+ g_assert(bytes_skipped > 0);
+ }
+ else
+ bytes_skipped = 0;
+
+ /* Get first segment of any kind */
+ any_seg = real->segment->next;
+ /* skip non-indexable segments, if any */
+ seg = any_seg;
+ while (seg != NULL && seg->char_count == 0)
+ seg = seg->next;
+
+ if (seg != NULL)
+ {
+ real->any_segment = any_seg;
+ real->segment = seg;
+
+ if (real->line_byte_offset >= 0)
+ {
+ g_assert(bytes_skipped > 0);
+ real->segment_byte_offset = 0;
+ real->line_byte_offset += bytes_skipped;
+ }
+
+ if (real->line_char_offset >= 0)
+ {
+ g_assert(chars_skipped > 0);
+ real->segment_char_offset = 0;
+ real->line_char_offset += chars_skipped;
+ adjust_char_index(real, chars_skipped);
+ }
+
+ check_invariants(iter);
+
+ return TRUE;
+ }
+ else
+ {
+ /* End of the line */
+ if (forward_line_leaving_caches_unmodified(real))
+ {
+ adjust_line_number(real, 1);
+ if (real->line_char_offset >= 0)
+ adjust_char_index(real, chars_skipped);
+
+ check_invariants(iter);
+
+ g_assert(real->line_byte_offset == 0);
+ g_assert(real->line_char_offset == 0);
+ g_assert(real->segment_byte_offset == 0);
+ g_assert(real->segment_char_offset == 0);
+ g_assert(gtk_text_iter_starts_line(iter));
+
+ check_invariants(iter);
+
+ return TRUE;
+ }
+ else
+ {
+ /* End of buffer */
+
+ check_invariants(iter);
+
+ return FALSE;
+ }
+ }
+}
+
+gboolean
+gtk_text_iter_backward_indexable_segment(GtkTextIter *iter)
+{
+ g_warning("FIXME");
+
+}
+
+gboolean
+gtk_text_iter_forward_char(GtkTextIter *iter)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return FALSE;
+ else
+ {
+ check_invariants(iter);
+ return forward_char(real);
+ }
+}
+
+gboolean
+gtk_text_iter_backward_char(GtkTextIter *iter)
+{
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ check_invariants(iter);
+
+ return gtk_text_iter_backward_chars(iter, 1);
+}
+
+/*
+ Definitely we should try to linear scan as often as possible for
+ movement within a single line, because we can't use the BTree to
+ speed within-line searches up; for movement between lines, we would
+ like to avoid the linear scan probably.
+
+ Instead of using this constant, it might be nice to cache the line
+ length in the iterator and linear scan if motion is within a single
+ line.
+
+ I guess you'd have to profile the various approaches.
+*/
+#define MAX_LINEAR_SCAN 300
+
+gboolean
+gtk_text_iter_forward_chars(GtkTextIter *iter, gint count)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return FALSE;
+ else if (count == 0)
+ return FALSE;
+ else if (count < 0)
+ return gtk_text_iter_backward_chars(iter, 0 - count);
+ else if (count < MAX_LINEAR_SCAN)
+ {
+ check_invariants(iter);
+
+ while (count > 1)
+ {
+ if (!forward_char(real))
+ return FALSE;
+ --count;
+ }
+
+ return forward_char(real);
+ }
+ else
+ {
+ gint current_char_index;
+ gint new_char_index;
+
+ check_invariants(iter);
+
+ current_char_index = gtk_text_iter_get_char_index(iter);
+
+ if (current_char_index == gtk_text_btree_char_count(real->tree))
+ return FALSE; /* can't move forward */
+
+ new_char_index = current_char_index + count;
+ gtk_text_iter_set_char_index(iter, new_char_index);
+
+ check_invariants(iter);
+
+ return TRUE;
+ }
+}
+
+gboolean
+gtk_text_iter_backward_chars(GtkTextIter *iter, gint count)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return FALSE;
+ else if (count == 0)
+ return FALSE;
+ else if (count < 0)
+ return gtk_text_iter_forward_chars(iter, 0 - count);
+
+ ensure_char_offsets(real);
+ check_invariants(iter);
+
+ if (count <= real->segment_char_offset)
+ {
+ /* Optimize the within-segment case */
+ g_assert(real->segment->char_count > 0);
+ g_assert(real->segment->type == &gtk_text_char_type);
+
+ real->segment_char_offset -= count;
+ g_assert(real->segment_char_offset >= 0);
+
+ if (real->line_byte_offset >= 0)
+ {
+ gint new_byte_offset;
+ gint i;
+
+ new_byte_offset = 0;
+ i = 0;
+ while (i < real->segment_char_offset)
+ {
+ GtkTextUniChar ch;
+ new_byte_offset +=
+ gtk_text_utf_to_unichar(real->segment->body.chars + new_byte_offset,
+ &ch);
+ ++i;
+ }
+
+ real->line_byte_offset -= (real->segment_byte_offset - new_byte_offset);
+ real->segment_byte_offset = new_byte_offset;
+ }
+
+ real->line_char_offset -= count;
+
+ adjust_char_index(real, 0 - count);
+
+ check_invariants(iter);
+
+ return TRUE;
+ }
+ else
+ {
+ /* We need to go back into previous segments. For now,
+ just keep this really simple. */
+ gint current_char_index;
+ gint new_char_index;
+
+ current_char_index = gtk_text_iter_get_char_index(iter);
+
+ if (current_char_index == 0)
+ return FALSE; /* can't move backward */
+
+ new_char_index = current_char_index - count;
+ if (new_char_index < 0)
+ new_char_index = 0;
+ gtk_text_iter_set_char_index(iter, new_char_index);
+
+ check_invariants(iter);
+
+ return TRUE;
+ }
+}
+
+gboolean
+gtk_text_iter_forward_line(GtkTextIter *iter)
+{
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return FALSE;
+
+ check_invariants(iter);
+
+ if (forward_line_leaving_caches_unmodified(real))
+ {
+ invalidate_char_index(real);
+ adjust_line_number(real, 1);
+
+ check_invariants(iter);
+
+ return TRUE;
+ }
+ else
+ {
+ check_invariants(iter);
+ return FALSE;
+ }
+}
+
+gboolean
+gtk_text_iter_backward_line(GtkTextIter *iter)
+{
+ GtkTextLine *new_line;
+ GtkTextRealIter *real;
+ gboolean offset_will_change;
+ gint offset;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return FALSE;
+
+ check_invariants(iter);
+
+ new_line = gtk_text_line_previous(real->line);
+
+ offset_will_change = FALSE;
+ if (real->line_char_offset > 0)
+ offset_will_change = TRUE;
+
+ if (new_line != NULL)
+ {
+ real->line = new_line;
+
+ adjust_line_number(real, -1);
+ }
+ else
+ {
+ if (!offset_will_change)
+ return FALSE;
+ }
+
+ invalidate_char_index(real);
+
+ real->line_byte_offset = 0;
+ real->line_char_offset = 0;
+
+ real->segment_byte_offset = 0;
+ real->segment_char_offset = 0;
+
+ /* Find first segment in line */
+ real->any_segment = real->line->segments;
+ real->segment = gtk_text_line_byte_to_segment(real->line,
+ 0, &offset);
+
+ g_assert(offset == 0);
+
+ /* Note that if we are on the first line, we snap to the start
+ of the first line and return TRUE, so TRUE means the
+ iterator changed, not that the line changed; this is maybe
+ a bit weird. I'm not sure there's an obvious right thing
+ to do though. */
+
+ check_invariants(iter);
+
+ return TRUE;
+}
+
+gboolean
+gtk_text_iter_forward_lines(GtkTextIter *iter, gint count)
+{
+ if (count < 0)
+ return gtk_text_iter_backward_lines(iter, 0 - count);
+ else if (count == 0)
+ return FALSE;
+ else if (count == 1)
+ {
+ check_invariants(iter);
+ return gtk_text_iter_forward_line(iter);
+ }
+ else
+ {
+ gint old_line;
+
+ old_line = gtk_text_iter_get_line_number(iter);
+
+ gtk_text_iter_set_line_number(iter, old_line + count);
+
+ check_invariants(iter);
+
+ return (gtk_text_iter_get_line_number(iter) != old_line);
+ }
+}
+
+gboolean
+gtk_text_iter_backward_lines(GtkTextIter *iter, gint count)
+{
+ if (count < 0)
+ return gtk_text_iter_forward_lines(iter, 0 - count);
+ else if (count == 0)
+ return FALSE;
+ else if (count == 1)
+ {
+ return gtk_text_iter_backward_line(iter);
+ }
+ else
+ {
+ gint old_line;
+
+ old_line = gtk_text_iter_get_line_number(iter);
+
+ gtk_text_iter_set_line_number(iter, MAX(old_line - count, 0));
+
+ return (gtk_text_iter_get_line_number(iter) != old_line);
+ }
+}
+
+static gboolean
+is_word_char(GtkTextUniChar ch, gpointer user_data)
+{
+ /* will likely need some i18n help FIXME */
+ return isalpha(ch);
+}
+
+static gboolean
+is_not_word_char(GtkTextUniChar ch, gpointer user_data)
+{
+ return !is_word_char(ch, user_data);
+}
+
+static gboolean
+gtk_text_iter_is_in_word(const GtkTextIter *iter)
+{
+ gint ch;
+
+ ch = gtk_text_iter_get_char(iter);
+
+ return is_word_char(ch, NULL);
+}
+
+gboolean
+gtk_text_iter_forward_word_end(GtkTextIter *iter)
+{
+ gboolean in_word;
+ GtkTextIter start;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ start = *iter;
+
+ in_word = gtk_text_iter_is_in_word(iter);
+
+ if (!in_word)
+ {
+ if (!gtk_text_iter_forward_find_char(iter, is_word_char, NULL))
+ return !gtk_text_iter_equal(iter, &start);
+ else
+ in_word = TRUE;
+ }
+
+ g_assert(in_word);
+
+ gtk_text_iter_forward_find_char(iter, is_not_word_char, NULL);
+
+ return !gtk_text_iter_equal(iter, &start);
+}
+
+gboolean
+gtk_text_iter_backward_word_start(GtkTextIter *iter)
+{
+ gboolean in_word;
+ GtkTextIter start;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ start = *iter;
+
+ in_word = gtk_text_iter_is_in_word(iter);
+
+ if (!in_word)
+ {
+ if (!gtk_text_iter_backward_find_char(iter, is_word_char, NULL))
+ return !gtk_text_iter_equal(iter, &start);
+ else
+ in_word = TRUE;
+ }
+
+ g_assert(in_word);
+
+ gtk_text_iter_backward_find_char(iter, is_not_word_char, NULL);
+ gtk_text_iter_forward_char(iter); /* point to first char of word,
+ not first non-word char. */
+
+ return !gtk_text_iter_equal(iter, &start);
+}
+
+gboolean
+gtk_text_iter_forward_word_ends(GtkTextIter *iter,
+ gint count)
+{
+ g_return_val_if_fail(iter != NULL, FALSE);
+ g_return_val_if_fail(count > 0, FALSE);
+
+ if (!gtk_text_iter_forward_word_end(iter))
+ return FALSE;
+ --count;
+
+ while (count > 0)
+ {
+ if (!gtk_text_iter_forward_word_end(iter))
+ break;
+ --count;
+ }
+ return TRUE;
+}
+
+gboolean
+gtk_text_iter_backward_word_starts(GtkTextIter *iter,
+ gint count)
+{
+ g_return_val_if_fail(iter != NULL, FALSE);
+ g_return_val_if_fail(count > 0, FALSE);
+
+ if (!gtk_text_iter_backward_word_start(iter))
+ return FALSE;
+ --count;
+
+ while (count > 0)
+ {
+ if (!gtk_text_iter_backward_word_start(iter))
+ break;
+ --count;
+ }
+ return TRUE;
+}
+
+/* up/down lines maintain the char offset, while forward/backward lines
+ always sets the char offset to 0. */
+gboolean
+gtk_text_iter_up_lines (GtkTextIter *iter,
+ gint count)
+{
+ gint char_offset;
+
+ if (count < 0)
+ return gtk_text_iter_down_lines(iter, 0 - count);
+
+ char_offset = gtk_text_iter_get_line_char(iter);
+
+ if (!gtk_text_iter_backward_line(iter))
+ return FALSE;
+ --count;
+
+ while (count > 0)
+ {
+ if (!gtk_text_iter_backward_line(iter))
+ break;
+ --count;
+ }
+
+ gtk_text_iter_set_line_char(iter, char_offset);
+
+ return TRUE;
+}
+
+gboolean
+gtk_text_iter_down_lines (GtkTextIter *iter,
+ gint count)
+{
+ gint char_offset;
+
+ if (count < 0)
+ return gtk_text_iter_up_lines(iter, 0 - count);
+
+ char_offset = gtk_text_iter_get_line_char(iter);
+
+ if (!gtk_text_iter_forward_line(iter))
+ return FALSE;
+ --count;
+
+ while (count > 0)
+ {
+ if (!gtk_text_iter_forward_line(iter))
+ break;
+ --count;
+ }
+
+ gtk_text_iter_set_line_char(iter, char_offset);
+
+ return TRUE;
+}
+
+void
+gtk_text_iter_set_line_char(GtkTextIter *iter,
+ gint char_on_line)
+{
+ GtkTextRealIter *real;
+
+ g_return_if_fail(iter != NULL);
+
+ real = gtk_text_iter_make_surreal(iter);
+
+ if (real == NULL)
+ return;
+
+ check_invariants(iter);
+
+ iter_set_from_char_offset(real, real->line, char_on_line);
+
+ check_invariants(iter);
+}
+
+void
+gtk_text_iter_set_line_number(GtkTextIter *iter, gint line_number)
+{
+ GtkTextLine *line;
+ gint real_line;
+ GtkTextRealIter *real;
+
+ g_return_if_fail(iter != NULL);
+
+ real = gtk_text_iter_make_surreal(iter);
+
+ if (real == NULL)
+ return;
+
+ check_invariants(iter);
+
+ line = gtk_text_btree_get_line(real->tree, line_number, &real_line);
+
+ iter_set_from_char_offset(real, line, 0);
+
+ /* We might as well cache this, since we know it. */
+ real->cached_line_number = real_line;
+
+ check_invariants(iter);
+}
+
+void
+gtk_text_iter_set_char_index(GtkTextIter *iter, gint char_index)
+{
+ GtkTextLine *line;
+ GtkTextRealIter *real;
+ gint line_start;
+ gint real_char_index;
+
+ g_return_if_fail(iter != NULL);
+
+ real = gtk_text_iter_make_surreal(iter);
+
+ if (real == NULL)
+ return;
+
+ check_invariants(iter);
+
+ if (real->cached_char_index >= 0 &&
+ real->cached_char_index == char_index)
+ return;
+
+ line = gtk_text_btree_get_line_at_char(real->tree,
+ char_index,
+ &line_start,
+ &real_char_index);
+
+ iter_set_from_char_offset(real, line, real_char_index - line_start);
+
+ /* Go ahead and cache this since we have it. */
+ real->cached_char_index = real_char_index;
+
+ check_invariants(iter);
+}
+
+void
+gtk_text_iter_forward_to_end (GtkTextIter *iter)
+{
+ GtkTextBuffer *buffer;
+ GtkTextRealIter *real;
+
+ g_return_if_fail(iter != NULL);
+
+ real = gtk_text_iter_make_surreal(iter);
+
+ if (real == NULL)
+ return;
+
+ buffer = gtk_text_btree_get_buffer(real->tree);
+
+ gtk_text_buffer_get_last_iter(buffer, iter);
+}
+
+gboolean
+gtk_text_iter_forward_to_newline(GtkTextIter *iter)
+{
+ gint current_offset;
+ gint new_offset;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ current_offset = gtk_text_iter_get_line_char(iter);
+ new_offset = gtk_text_iter_get_chars_in_line(iter) - 1;
+
+ if (current_offset < new_offset)
+ {
+ /* Move to end of this line. */
+ gtk_text_iter_set_line_char(iter, new_offset);
+ return TRUE;
+ }
+ else
+ {
+ /* Move to end of next line. */
+ if (gtk_text_iter_forward_line(iter))
+ {
+ gtk_text_iter_forward_to_newline(iter);
+ return TRUE;
+ }
+ else
+ return FALSE;
+ }
+}
+
+gboolean
+gtk_text_iter_forward_find_tag_toggle (GtkTextIter *iter,
+ GtkTextTag *tag)
+{
+ GtkTextLine *next_line;
+ GtkTextLine *current_line;
+ GtkTextRealIter *real;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+
+ real = gtk_text_iter_make_real(iter);
+
+ if (real == NULL)
+ return FALSE;
+
+ check_invariants(iter);
+
+ current_line = real->line;
+ next_line = gtk_text_line_next_could_contain_tag(current_line,
+ real->tree, tag);
+
+ while (gtk_text_iter_forward_indexable_segment(iter))
+ {
+ /* If we went forward to a line that couldn't contain a toggle
+ for the tag, then skip forward to a line that could contain
+ it. This potentially skips huge hunks of the tree, so we
+ aren't a purely linear search. */
+ if (real->line != current_line)
+ {
+ if (next_line == NULL)
+ {
+ /* End of search. Set to end of buffer. */
+ gtk_text_btree_get_last_iter(real->tree, iter);
+ return FALSE;
+ }
+
+ if (real->line != next_line)
+ iter_set_from_byte_offset(real, next_line, 0);
+
+ current_line = real->line;
+ next_line = gtk_text_line_next_could_contain_tag(current_line,
+ real->tree,
+ tag);
+ }
+
+ if (gtk_text_iter_toggles_tag(iter, tag))
+ {
+ /* If there's a toggle here, it isn't indexable so
+ any_segment can't be the indexable segment. */
+ g_assert(real->any_segment != real->segment);
+ return TRUE;
+ }
+ }
+
+ /* Reached end of buffer */
+ return FALSE;
+}
+
+gboolean
+gtk_text_iter_backward_find_tag_toggle (GtkTextIter *iter,
+ GtkTextTag *tag)
+{
+
+ g_warning("FIXME");
+}
+
+static gboolean
+matches_pred(GtkTextIter *iter,
+ GtkTextViewCharPredicate pred,
+ gpointer user_data)
+{
+ gint ch;
+
+ ch = gtk_text_iter_get_char(iter);
+
+ return (*pred) (ch, user_data);
+}
+
+gboolean
+gtk_text_iter_forward_find_char (GtkTextIter *iter,
+ GtkTextViewCharPredicate pred,
+ gpointer user_data)
+{
+ g_return_val_if_fail(iter != NULL, FALSE);
+ g_return_val_if_fail(pred != NULL, FALSE);
+
+ while (gtk_text_iter_forward_char(iter))
+ {
+ if (matches_pred(iter, pred, user_data))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gboolean
+gtk_text_iter_backward_find_char (GtkTextIter *iter,
+ GtkTextViewCharPredicate pred,
+ gpointer user_data)
+{
+ g_return_val_if_fail(iter != NULL, FALSE);
+ g_return_val_if_fail(pred != NULL, FALSE);
+
+ while (gtk_text_iter_backward_char(iter))
+ {
+ if (matches_pred(iter, pred, user_data))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+/*
+ * Comparisons
+ */
+
+gboolean
+gtk_text_iter_equal(const GtkTextIter *lhs, const GtkTextIter *rhs)
+{
+ GtkTextRealIter *real_lhs;
+ GtkTextRealIter *real_rhs;
+
+ real_lhs = (GtkTextRealIter*)lhs;
+ real_rhs = (GtkTextRealIter*)rhs;
+
+ check_invariants(lhs);
+ check_invariants(rhs);
+
+ if (real_lhs->line != real_rhs->line)
+ return FALSE;
+ else if (real_lhs->line_byte_offset >= 0 &&
+ real_rhs->line_byte_offset >= 0)
+ return real_lhs->line_byte_offset == real_rhs->line_byte_offset;
+ else
+ {
+ /* the ensure_char_offsets() calls do nothing if the char offsets
+ are already up-to-date. */
+ ensure_char_offsets(real_lhs);
+ ensure_char_offsets(real_rhs);
+ return real_lhs->line_char_offset == real_rhs->line_char_offset;
+ }
+}
+
+gint
+gtk_text_iter_compare(const GtkTextIter *lhs, const GtkTextIter *rhs)
+{
+ GtkTextRealIter *real_lhs;
+ GtkTextRealIter *real_rhs;
+
+ real_lhs = gtk_text_iter_make_surreal(lhs);
+ real_rhs = gtk_text_iter_make_surreal(rhs);
+
+ check_invariants(lhs);
+ check_invariants(rhs);
+
+ if (real_lhs == NULL ||
+ real_rhs == NULL)
+ return -1; /* why not */
+
+ if (real_lhs->line == real_rhs->line)
+ {
+ gint left_index, right_index;
+
+ if (real_lhs->line_byte_offset >= 0 &&
+ real_rhs->line_byte_offset >= 0)
+ {
+ left_index = real_lhs->line_byte_offset;
+ right_index = real_rhs->line_byte_offset;
+ }
+ else
+ {
+ /* the ensure_char_offsets() calls do nothing if
+ the offsets are already up-to-date. */
+ ensure_char_offsets(real_lhs);
+ ensure_char_offsets(real_rhs);
+ left_index = real_lhs->line_char_offset;
+ right_index = real_rhs->line_char_offset;
+ }
+
+ if (left_index < right_index)
+ return -1;
+ else if (left_index > right_index)
+ return 1;
+ else
+ return 0;
+ }
+ else
+ {
+ gint line1, line2;
+
+ line1 = gtk_text_iter_get_line_number(lhs);
+ line2 = gtk_text_iter_get_line_number(rhs);
+ if (line1 < line2)
+ return -1;
+ else if (line1 > line2)
+ return 1;
+ else
+ return 0;
+ }
+}
+
+gboolean
+gtk_text_iter_in_region (const GtkTextIter *iter,
+ const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ return gtk_text_iter_compare(iter, start) >= 0 &&
+ gtk_text_iter_compare(iter, end) < 0;
+}
+
+void
+gtk_text_iter_reorder (GtkTextIter *first,
+ GtkTextIter *second)
+{
+ g_return_if_fail(first != NULL);
+ g_return_if_fail(second != NULL);
+
+ if (gtk_text_iter_compare(first, second) > 0)
+ {
+ GtkTextIter tmp;
+
+ tmp = *first;
+ *first = *second;
+ *second = tmp;
+ }
+}
+
+/*
+ * Init iterators from the BTree
+ */
+
+void
+gtk_text_btree_get_iter_at_char (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ gint char_index)
+{
+ GtkTextRealIter *real = (GtkTextRealIter*)iter;
+ gint real_char_index;
+ gint line_start;
+ GtkTextLine *line;
+
+ g_return_if_fail(iter != NULL);
+ g_return_if_fail(tree != NULL);
+
+ line = gtk_text_btree_get_line_at_char(tree, char_index,
+ &line_start, &real_char_index);
+
+ iter_init_from_char_offset(iter, tree, line, real_char_index - line_start);
+
+ real->cached_char_index = real_char_index;
+
+ check_invariants(iter);
+}
+
+void
+gtk_text_btree_get_iter_at_line_char (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ gint line_number,
+ gint char_on_line)
+{
+ GtkTextRealIter *real = (GtkTextRealIter*)iter;
+ GtkTextLine *line;
+ gint real_line;
+
+ g_return_if_fail(iter != NULL);
+ g_return_if_fail(tree != NULL);
+
+ line = gtk_text_btree_get_line(tree, line_number, &real_line);
+
+ iter_init_from_char_offset(iter, tree, line, char_on_line);
+
+ /* We might as well cache this, since we know it. */
+ real->cached_line_number = real_line;
+
+ check_invariants(iter);
+}
+
+void
+gtk_text_btree_get_iter_at_line_byte (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ gint line_number,
+ gint byte_index)
+{
+ GtkTextRealIter *real = (GtkTextRealIter*)iter;
+ GtkTextLine *line;
+ gint real_line;
+
+ g_return_if_fail(iter != NULL);
+ g_return_if_fail(tree != NULL);
+
+ line = gtk_text_btree_get_line(tree, line_number, &real_line);
+
+ iter_init_from_byte_offset(iter, tree, line, byte_index);
+
+ /* We might as well cache this, since we know it. */
+ real->cached_line_number = real_line;
+
+ check_invariants(iter);
+}
+
+void
+gtk_text_btree_get_iter_at_line (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ GtkTextLine *line,
+ gint byte_offset)
+{
+ g_return_if_fail(iter != NULL);
+ g_return_if_fail(tree != NULL);
+ g_return_if_fail(line != NULL);
+
+ iter_init_from_byte_offset(iter, tree, line, byte_offset);
+
+ check_invariants(iter);
+}
+
+gboolean
+gtk_text_btree_get_iter_at_first_toggle (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ GtkTextTag *tag)
+{
+ GtkTextLine *line;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+ g_return_val_if_fail(tree != NULL, FALSE);
+
+ line = gtk_text_btree_first_could_contain_tag(tree, tag);
+
+ if (line == NULL)
+ {
+ /* Set iter to last in tree */
+ gtk_text_btree_get_last_iter(tree, iter);
+ check_invariants(iter);
+ return FALSE;
+ }
+ else
+ {
+ iter_init_from_byte_offset(iter, tree, line, 0);
+ gtk_text_iter_forward_find_tag_toggle(iter, tag);
+ check_invariants(iter);
+ return TRUE;
+ }
+}
+
+gboolean
+gtk_text_btree_get_iter_at_last_toggle (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ GtkTextTag *tag)
+{
+ GtkTextLine *line;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+ g_return_val_if_fail(tree != NULL, FALSE);
+
+ line = gtk_text_btree_last_could_contain_tag(tree, tag);
+
+ if (line == NULL)
+ {
+ /* Set iter to first in tree */
+ gtk_text_btree_get_iter_at_line_char(tree, iter, 0, 0);
+ check_invariants(iter);
+ return FALSE;
+ }
+ else
+ {
+ iter_init_from_byte_offset(iter, tree, line, -1);
+ gtk_text_iter_backward_find_tag_toggle(iter, tag);
+ check_invariants(iter);
+ return TRUE;
+ }
+}
+
+gboolean
+gtk_text_btree_get_iter_from_string (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ const gchar *string)
+{
+ g_return_val_if_fail(iter != NULL, FALSE);
+ g_return_val_if_fail(tree != NULL, FALSE);
+
+ g_warning("FIXME");
+}
+
+gboolean
+gtk_text_btree_get_iter_at_mark_name (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ const gchar *mark_name)
+{
+ GtkTextLineSegment *mark;
+
+ g_return_val_if_fail(iter != NULL, FALSE);
+ g_return_val_if_fail(tree != NULL, FALSE);
+
+ mark = gtk_text_btree_get_mark_by_name(tree, mark_name);
+
+ if (mark == NULL)
+ return FALSE;
+ else
+ {
+ gtk_text_btree_get_iter_at_mark(tree, iter, mark);
+ check_invariants(iter);
+ return TRUE;
+ }
+}
+
+void
+gtk_text_btree_get_iter_at_mark (GtkTextBTree *tree,
+ GtkTextIter *iter,
+ GtkTextLineSegment *mark)
+{
+ g_return_if_fail(iter != NULL);
+ g_return_if_fail(tree != NULL);
+ g_return_if_fail(mark->type == &gtk_text_left_mark_type ||
+ mark->type == &gtk_text_right_mark_type);
+
+ iter_init_from_segment(iter, tree, mark->body.mark.line, mark);
+ g_assert(mark->body.mark.line == gtk_text_iter_get_line(iter));
+ check_invariants(iter);
+}
+
+void
+gtk_text_btree_get_last_iter (GtkTextBTree *tree,
+ GtkTextIter *iter)
+{
+ g_return_if_fail(iter != NULL);
+ g_return_if_fail(tree != NULL);
+
+ gtk_text_btree_get_iter_at_char(tree,
+ iter,
+ gtk_text_btree_char_count(tree));
+ check_invariants(iter);
+}
+
+void
+gtk_text_iter_spew (const GtkTextIter *iter, const gchar *desc)
+{
+ GtkTextRealIter *real = (GtkTextRealIter*)iter;
+
+ g_return_if_fail(iter != NULL);
+
+ if (real->chars_changed_stamp != gtk_text_btree_get_chars_changed_stamp(real->tree))
+ g_print(" %20s: <invalidated iterator>\n", desc);
+ else
+ {
+ check_invariants(iter);
+ g_print(" %20s: line %d / char %d / line char %d / line byte %d\n",
+ desc,
+ gtk_text_iter_get_line_number(iter),
+ gtk_text_iter_get_char_index(iter),
+ gtk_text_iter_get_line_char(iter),
+ gtk_text_iter_get_line_byte(iter));
+ check_invariants(iter);
+ }
+}
+
+void
+gtk_text_iter_check(const GtkTextIter *iter)
+{
+ const GtkTextRealIter *real = (const GtkTextRealIter*)iter;
+ gint line_char_offset, line_byte_offset, seg_char_offset, seg_byte_offset;
+ GtkTextLineSegment *byte_segment;
+ GtkTextLineSegment *byte_any_segment;
+ GtkTextLineSegment *char_segment;
+ GtkTextLineSegment *char_any_segment;
+ gboolean segments_updated;
+
+ /* We are going to check our class invariants for the Iter class. */
+
+ if (real->chars_changed_stamp !=
+ gtk_text_btree_get_chars_changed_stamp(real->tree))
+ g_error("iterator check failed: invalid iterator");
+
+ if (real->line_char_offset < 0 && real->line_byte_offset < 0)
+ g_error("iterator check failed: both char and byte offsets are invalid");
+
+ segments_updated = (real->segments_changed_stamp ==
+ gtk_text_btree_get_segments_changed_stamp(real->tree));
+
+#if 0
+ printf("checking iter, segments %s updated, byte %d char %d\n",
+ segments_updated ? "are" : "aren't",
+ real->line_byte_offset,
+ real->line_char_offset);
+#endif
+
+ if (real->line_byte_offset == 97 &&
+ real->line_char_offset == 95)
+ G_BREAKPOINT();
+
+ if (segments_updated)
+ {
+ if (real->segment_char_offset < 0 && real->segment_byte_offset < 0)
+ g_error("iterator check failed: both char and byte segment offsets are invalid");
+
+ if (real->segment->char_count == 0)
+ g_error("iterator check failed: segment is not indexable.");
+
+ if (real->line_char_offset >= 0 && real->segment_char_offset < 0)
+ g_error("segment char offset is not properly up-to-date");
+
+ if (real->line_byte_offset >= 0 && real->segment_byte_offset < 0)
+ g_error("segment byte offset is not properly up-to-date");
+
+ if (real->segment_byte_offset >= 0 &&
+ real->segment_byte_offset >= real->segment->byte_count)
+ g_error("segment byte offset is too large.");
+
+ if (real->segment_char_offset >= 0 &&
+ real->segment_char_offset >= real->segment->char_count)
+ g_error("segment char offset is too large.");
+ }
+
+ if (real->line_byte_offset >= 0)
+ {
+ gtk_text_line_byte_locate(real->line, real->line_byte_offset,
+ &byte_segment, &byte_any_segment,
+ &seg_byte_offset, &line_byte_offset);
+
+ if (line_byte_offset != real->line_byte_offset)
+ g_error("wrong byte offset was stored in iterator");
+
+ if (segments_updated)
+ {
+ if (real->segment != byte_segment)
+ g_error("wrong segment was stored in iterator");
+
+ if (real->any_segment != byte_any_segment)
+ g_error("wrong any_segment was stored in iterator");
+
+ if (seg_byte_offset != real->segment_byte_offset)
+ g_error("wrong segment byte offset was stored in iterator");
+ }
+ }
+
+ if (real->line_char_offset >= 0)
+ {
+ gtk_text_line_char_locate(real->line, real->line_char_offset,
+ &char_segment, &char_any_segment,
+ &seg_char_offset, &line_char_offset);
+
+ if (line_char_offset != real->line_char_offset)
+ g_error("wrong char offset was stored in iterator");
+
+ if (segments_updated)
+ {
+ if (real->segment != char_segment)
+ g_error("wrong segment was stored in iterator");
+
+ if (real->any_segment != char_any_segment)
+ g_error("wrong any_segment was stored in iterator");
+
+ if (seg_char_offset != real->segment_char_offset)
+ g_error("wrong segment char offset was stored in iterator");
+ }
+ }
+
+ if (real->line_char_offset >= 0 && real->line_byte_offset >= 0)
+ {
+ if (byte_segment != char_segment)
+ g_error("char and byte offsets did not point to the same segment");
+
+ if (byte_any_segment != char_any_segment)
+ g_error("char and byte offsets did not point to the same any segment");
+
+ /* Make sure the segment offsets are equivalent, if it's a char
+ segment. */
+ if (char_segment->type == &gtk_text_char_type)
+ {
+ gint byte_offset = 0;
+ gint char_offset = 0;
+ while (char_offset < seg_char_offset)
+ {
+ GtkTextUniChar ch;
+ byte_offset +=
+ gtk_text_utf_to_unichar(char_segment->body.chars + byte_offset,
+ &ch);
+ char_offset += 1;
+ }
+
+ if (byte_offset != seg_byte_offset)
+ g_error("byte offset did not correspond to char offset");
+
+ char_offset =
+ gtk_text_view_num_utf_chars(char_segment->body.chars,
+ seg_byte_offset);
+
+ if (char_offset != seg_char_offset)
+ g_error("char offset did not correspond to byte offset");
+ }
+ }
+
+ if (real->cached_line_number >= 0)
+ {
+ gint should_be;
+
+ should_be = gtk_text_line_get_number(real->line);
+ if (real->cached_line_number != should_be)
+ g_error("wrong line number was cached");
+ }
+
+ if (real->cached_char_index >= 0)
+ {
+ if (real->line_char_offset >= 0) /* only way we can check it
+ efficiently, not a real
+ invariant. */
+ {
+ gint char_index;
+
+ char_index = gtk_text_line_char_index(real->line);
+ char_index += real->line_char_offset;
+
+ if (real->cached_char_index != char_index)
+ g_error("wrong char index was cached");
+ }
+ }
+}
+
diff --git a/gtk/gtktextiter.h b/gtk/gtktextiter.h
new file mode 100644
index 000000000..579fe859b
--- /dev/null
+++ b/gtk/gtktextiter.h
@@ -0,0 +1,182 @@
+#ifndef GTK_TEXT_ITER_H
+#define GTK_TEXT_ITER_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Iter: represents a location in the text. Becomes invalid if the
+ * characters/pixmaps/widgets (indexable objects) in the text buffer
+ * are changed.
+ */
+
+#include <gtk/gtktexttag.h>
+
+typedef struct _GtkTextBuffer GtkTextBuffer;
+
+struct _GtkTextIter {
+ gpointer dummy1;
+ gpointer dummy2;
+ gint dummy3;
+ gint dummy4;
+ gint dummy10;
+ gint dummy11;
+ gint dummy5;
+ gint dummy6;
+ gpointer dummy7;
+ gpointer dummy8;
+ gint dummy9;
+ gpointer pad1;
+ gint pad2;
+};
+
+
+/* This is primarily intended for language bindings that want to avoid
+ a "buffer" argument to text insertions, deletions, etc. */
+GtkTextBuffer *gtk_text_iter_get_buffer(const GtkTextIter *iter);
+
+/*
+ * Life cycle
+ */
+
+GtkTextIter *gtk_text_iter_copy (const GtkTextIter *iter);
+void gtk_text_iter_free (GtkTextIter *iter);
+
+/*
+ * Convert to different kinds of index
+ */
+gint gtk_text_iter_get_char_index (const GtkTextIter *iter);
+gint gtk_text_iter_get_line_number (const GtkTextIter *iter);
+gint gtk_text_iter_get_line_char (const GtkTextIter *iter);
+gint gtk_text_iter_get_line_byte (const GtkTextIter *iter);
+
+/*
+ * "Dereference" operators
+ */
+gint gtk_text_iter_get_char (const GtkTextIter *iter);
+
+/* includes the 0xFFFD char for pixmaps/widgets, so char offsets
+ into the returned string map properly into buffer char offsets */
+gchar *gtk_text_iter_get_slice (const GtkTextIter *start,
+ const GtkTextIter *end);
+
+/* includes only text, no 0xFFFD */
+gchar *gtk_text_iter_get_text (const GtkTextIter *start,
+ const GtkTextIter *end);
+/* exclude invisible chars */
+gchar *gtk_text_iter_get_visible_slice (const GtkTextIter *start,
+ const GtkTextIter *end);
+gchar *gtk_text_iter_get_visible_text (const GtkTextIter *start,
+ const GtkTextIter *end);
+
+/* Returns TRUE if the iterator pointed at a pixmap */
+gboolean gtk_text_iter_get_pixmap (const GtkTextIter *iter,
+ GdkPixmap **pixmap,
+ GdkBitmap **mask);
+
+/* Return list of tags toggled at this point (toggled_on determines
+ whether the list is of on-toggles or off-toggles) */
+GSList *gtk_text_iter_get_toggled_tags (const GtkTextIter *iter,
+ gboolean toggled_on);
+
+gboolean gtk_text_iter_begins_tag (const GtkTextIter *iter,
+ GtkTextTag *tag);
+
+gboolean gtk_text_iter_ends_tag (const GtkTextIter *iter,
+ GtkTextTag *tag);
+
+gboolean gtk_text_iter_toggles_tag (const GtkTextIter *iter,
+ GtkTextTag *tag);
+
+gboolean gtk_text_iter_has_tag (const GtkTextIter *iter,
+ GtkTextTag *tag);
+
+gboolean gtk_text_iter_starts_line (const GtkTextIter *iter);
+gboolean gtk_text_iter_ends_line (const GtkTextIter *iter);
+
+gint gtk_text_iter_get_chars_in_line (const GtkTextIter *iter);
+
+/*
+ * Moving around the buffer
+ */
+gboolean gtk_text_iter_forward_char (GtkTextIter *iter);
+gboolean gtk_text_iter_backward_char (GtkTextIter *iter);
+gboolean gtk_text_iter_forward_chars (GtkTextIter *iter,
+ gint count);
+gboolean gtk_text_iter_backward_chars (GtkTextIter *iter,
+ gint count);
+gboolean gtk_text_iter_forward_line (GtkTextIter *iter);
+gboolean gtk_text_iter_backward_line (GtkTextIter *iter);
+gboolean gtk_text_iter_forward_lines (GtkTextIter *iter,
+ gint count);
+gboolean gtk_text_iter_backward_lines (GtkTextIter *iter,
+ gint count);
+gboolean gtk_text_iter_forward_word_ends(GtkTextIter *iter,
+ gint count);
+gboolean gtk_text_iter_backward_word_starts(GtkTextIter *iter,
+ gint count);
+gboolean gtk_text_iter_forward_word_end(GtkTextIter *iter);
+gboolean gtk_text_iter_backward_word_start(GtkTextIter *iter);
+
+gboolean gtk_text_iter_up_lines (GtkTextIter *iter,
+ gint count);
+
+gboolean gtk_text_iter_down_lines (GtkTextIter *iter,
+ gint count);
+
+void gtk_text_iter_set_char_index (GtkTextIter *iter,
+ gint char_index);
+void gtk_text_iter_set_line_number (GtkTextIter *iter,
+ gint line_number);
+void gtk_text_iter_set_line_char (GtkTextIter *iter,
+ gint char_on_line);
+
+void gtk_text_iter_forward_to_end (GtkTextIter *iter);
+gboolean gtk_text_iter_forward_to_newline(GtkTextIter *iter);
+
+/* returns TRUE if a toggle was found; NULL for the tag pointer
+ means "any tag toggle", otherwise the next toggle of the
+ specified tag is located. */
+gboolean gtk_text_iter_forward_find_tag_toggle (GtkTextIter *iter,
+ GtkTextTag *tag);
+
+gboolean gtk_text_iter_backward_find_tag_toggle (GtkTextIter *iter,
+ GtkTextTag *tag);
+
+typedef gboolean (* GtkTextViewCharPredicate) (guint16 ch, gpointer user_data);
+
+gboolean gtk_text_iter_forward_find_char (GtkTextIter *iter,
+ GtkTextViewCharPredicate pred,
+ gpointer user_data);
+
+gboolean gtk_text_iter_backward_find_char (GtkTextIter *iter,
+ GtkTextViewCharPredicate pred,
+ gpointer user_data);
+
+/*
+ * Comparisons
+ */
+gboolean gtk_text_iter_equal (const GtkTextIter *lhs,
+ const GtkTextIter *rhs);
+gint gtk_text_iter_compare (const GtkTextIter *lhs,
+ const GtkTextIter *rhs);
+gboolean gtk_text_iter_in_region (const GtkTextIter *iter,
+ const GtkTextIter *start,
+ const GtkTextIter *end);
+
+/* Put these two in ascending order */
+void gtk_text_iter_reorder (GtkTextIter *first,
+ GtkTextIter *second);
+
+/* Debug */
+void gtk_text_iter_spew (const GtkTextIter *iter,
+ const gchar *desc);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
+
diff --git a/gtk/gtktextiterprivate.h b/gtk/gtktextiterprivate.h
new file mode 100644
index 000000000..4a83c643a
--- /dev/null
+++ b/gtk/gtktextiterprivate.h
@@ -0,0 +1,32 @@
+#ifndef GTK_TEXT_ITER_PRIVATE_H
+#define GTK_TEXT_ITER_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <gtk/gtktextiter.h>
+
+GtkTextLineSegment *gtk_text_iter_get_indexable_segment(const GtkTextIter *iter);
+GtkTextLineSegment *gtk_text_iter_get_any_segment(const GtkTextIter *iter);
+
+GtkTextLine *gtk_text_iter_get_line(const GtkTextIter *iter);
+
+GtkTextBTree *gtk_text_iter_get_btree(const GtkTextIter *iter);
+
+gboolean gtk_text_iter_forward_indexable_segment(GtkTextIter *iter);
+gboolean gtk_text_iter_backward_indexable_segment(GtkTextIter *iter);
+
+gint gtk_text_iter_get_segment_byte(const GtkTextIter *iter);
+gint gtk_text_iter_get_segment_char(const GtkTextIter *iter);
+
+/* debug */
+void gtk_text_iter_check(const GtkTextIter *iter);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
+
diff --git a/gtk/gtktextlayout.c b/gtk/gtktextlayout.c
new file mode 100644
index 000000000..6bf09515f
--- /dev/null
+++ b/gtk/gtktextlayout.c
@@ -0,0 +1,2159 @@
+/* gtktextlayout.c - calculate the layout of the text
+ *
+ * Copyright (c) 1992-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ * Copyright (c) 2000 Red Hat, Inc.
+ * Tk->Gtk port by Havoc Pennington
+ * Pango support by Owen Taylor
+ *
+ *
+ * This software is copyrighted by the Regents of the University of
+ * California, Sun Microsystems, Inc., and other parties. The
+ * following terms apply to all files associated with the software
+ * unless explicitly disclaimed in individual files.
+ *
+ * The authors hereby grant permission to use, copy, modify,
+ * distribute, and license this software and its documentation for any
+ * purpose, provided that existing copyright notices are retained in
+ * all copies and that this notice is included verbatim in any
+ * distributions. No written agreement, license, or royalty fee is
+ * required for any of the authorized uses. Modifications to this
+ * software may be copyrighted by their authors and need not follow
+ * the licensing terms described here, provided that the new terms are
+ * clearly indicated on the first page of each file where they apply.
+ *
+ * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+ * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
+ * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
+ * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
+ * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
+ * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * GOVERNMENT USE: If you are acquiring this software on behalf of the
+ * U.S. government, the Government shall have only "Restricted Rights"
+ * in the software and related documentation as defined in the Federal
+ * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
+ * are acquiring the software on behalf of the Department of Defense,
+ * the software shall be classified as "Commercial Computer Software"
+ * and the Government shall have only "Restricted Rights" as defined
+ * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
+ * foregoing, the authors grant the U.S. Government and others acting
+ * in its behalf permission to use and distribute the software in
+ * accordance with the terms specified in this license.
+ *
+ */
+
+#include "gtksignal.h"
+#include "gtktextlayout.h"
+#include "gtktextbtree.h"
+#include "gtktextiterprivate.h"
+
+#include <stdlib.h>
+
+static GtkTextLineData *gtk_text_line_data_new (GtkTextLayout *layout,
+ GtkTextLine *line);
+
+static GtkTextLineData *gtk_text_layout_real_wrap (GtkTextLayout *layout,
+ GtkTextLine *line,
+ /* may be NULL */
+ GtkTextLineData *line_data);
+
+static void gtk_text_layout_real_get_log_attrs (GtkTextLayout *layout,
+ GtkTextLine *line,
+ PangoLogAttr **attrs,
+ gint *n_attrs);
+
+static void gtk_text_layout_invalidated (GtkTextLayout *layout);
+
+static void gtk_text_layout_real_invalidate (GtkTextLayout *layout,
+ const GtkTextIter *start,
+ const GtkTextIter *end);
+static void gtk_text_layout_real_free_line_data (GtkTextLayout *layout,
+ GtkTextLine *line,
+ GtkTextLineData *line_data);
+
+static void gtk_text_layout_invalidate_all (GtkTextLayout *layout);
+
+static PangoAttribute *gtk_text_attr_appearance_new (const GtkTextAppearance *appearance);
+
+enum {
+ INVALIDATED,
+ CHANGED,
+ LAST_SIGNAL
+};
+
+enum {
+ ARG_0,
+ LAST_ARG
+};
+
+static void gtk_text_layout_init (GtkTextLayout *text_layout);
+static void gtk_text_layout_class_init (GtkTextLayoutClass *klass);
+static void gtk_text_layout_destroy (GtkObject *object);
+static void gtk_text_layout_finalize (GObject *object);
+
+void gtk_marshal_NONE__INT_INT_INT_INT (GtkObject *object,
+ GtkSignalFunc func,
+ gpointer func_data,
+ GtkArg *args);
+
+static GtkObjectClass *parent_class = NULL;
+static guint signals[LAST_SIGNAL] = { 0 };
+
+PangoAttrType gtk_text_attr_appearance_type = 0;
+
+GtkType
+gtk_text_layout_get_type (void)
+{
+ static GtkType our_type = 0;
+
+ if (our_type == 0)
+ {
+ static const GtkTypeInfo our_info =
+ {
+ "GtkTextLayout",
+ sizeof (GtkTextLayout),
+ sizeof (GtkTextLayoutClass),
+ (GtkClassInitFunc) gtk_text_layout_class_init,
+ (GtkObjectInitFunc) gtk_text_layout_init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL
+ };
+
+ our_type = gtk_type_unique (GTK_TYPE_OBJECT, &our_info);
+ }
+
+ return our_type;
+}
+
+static void
+gtk_text_layout_class_init (GtkTextLayoutClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
+
+ parent_class = gtk_type_class (GTK_TYPE_OBJECT);
+
+ signals[INVALIDATED] =
+ gtk_signal_new ("invalidated",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextLayoutClass, invalidated),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE,
+ 0);
+
+ signals[CHANGED] =
+ gtk_signal_new ("changed",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextLayoutClass, changed),
+ gtk_marshal_NONE__INT_INT_INT,
+ GTK_TYPE_NONE,
+ 3,
+ GTK_TYPE_INT,
+ GTK_TYPE_INT,
+ GTK_TYPE_INT);
+
+ gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+
+ object_class->destroy = gtk_text_layout_destroy;
+ gobject_class->finalize = gtk_text_layout_finalize;
+
+ klass->wrap = gtk_text_layout_real_wrap;
+ klass->get_log_attrs = gtk_text_layout_real_get_log_attrs;
+ klass->invalidate = gtk_text_layout_real_invalidate;
+ klass->free_line_data = gtk_text_layout_real_free_line_data;
+}
+
+void
+gtk_text_layout_init (GtkTextLayout *text_layout)
+{
+}
+
+GtkTextLayout*
+gtk_text_layout_new (void)
+{
+ return GTK_TEXT_LAYOUT (gtk_type_new (gtk_text_layout_get_type ()));
+}
+
+static void
+free_style_cache (GtkTextLayout *text_layout)
+{
+ if (text_layout->one_style_cache)
+ {
+ gtk_text_view_style_values_unref (text_layout->one_style_cache);
+ text_layout->one_style_cache = NULL;
+ }
+}
+
+static void
+gtk_text_layout_destroy (GtkObject *object)
+{
+ GtkTextLayout *layout;
+
+ layout = GTK_TEXT_LAYOUT (object);
+
+ gtk_text_layout_set_buffer (layout, NULL);
+
+ if (layout->default_style)
+ gtk_text_view_style_values_unref (layout->default_style);
+ layout->default_style = NULL;
+
+ if (layout->ltr_context)
+ {
+ pango_context_unref (layout->ltr_context);
+ layout->ltr_context = NULL;
+ }
+ if (layout->rtl_context)
+ {
+ pango_context_unref (layout->rtl_context);
+ layout->rtl_context = NULL;
+ }
+
+ (* parent_class->destroy) (object);
+}
+
+static void
+gtk_text_layout_finalize (GObject *object)
+{
+ GtkTextLayout *text_layout;
+
+ text_layout = GTK_TEXT_LAYOUT (object);
+
+ (* G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+void
+gtk_text_layout_set_buffer (GtkTextLayout *layout,
+ GtkTextBuffer *buffer)
+{
+ g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+ g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
+
+ if (layout->buffer == buffer)
+ return;
+
+ free_style_cache (layout);
+
+ if (layout->buffer)
+ {
+ gtk_text_btree_remove_view (layout->buffer->tree, layout);
+
+ gtk_object_unref (GTK_OBJECT (layout->buffer));
+ layout->buffer = NULL;
+ }
+
+ if (buffer)
+ {
+ layout->buffer = buffer;
+
+ gtk_object_sink (GTK_OBJECT (buffer));
+ gtk_object_ref (GTK_OBJECT (buffer));
+
+ gtk_text_btree_add_view (buffer->tree, layout);
+ }
+}
+
+void
+gtk_text_layout_default_style_changed (GtkTextLayout *layout)
+{
+ g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+
+ gtk_text_layout_invalidate_all (layout);
+}
+
+void
+gtk_text_layout_set_default_style (GtkTextLayout *layout,
+ GtkTextStyleValues *values)
+{
+ g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+ g_return_if_fail (values != NULL);
+
+ if (values == layout->default_style)
+ return;
+
+ gtk_text_view_style_values_ref (values);
+
+ if (layout->default_style)
+ gtk_text_view_style_values_unref (layout->default_style);
+
+ layout->default_style = values;
+
+ gtk_text_layout_default_style_changed (layout);
+}
+
+void
+gtk_text_layout_set_contexts (GtkTextLayout *layout,
+ PangoContext *ltr_context,
+ PangoContext *rtl_context)
+{
+ g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+
+ if (layout->ltr_context)
+ pango_context_unref (ltr_context);
+
+ layout->ltr_context = ltr_context;
+ pango_context_ref (ltr_context);
+
+ if (layout->rtl_context)
+ pango_context_unref (rtl_context);
+
+ layout->rtl_context = rtl_context;
+ pango_context_ref (rtl_context);
+
+ gtk_text_layout_invalidate_all (layout);
+}
+
+void
+gtk_text_layout_set_screen_width (GtkTextLayout *layout, gint width)
+{
+ g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+ g_return_if_fail (width >= 0);
+ g_return_if_fail (layout->wrap_loop_count == 0);
+
+ if (layout->screen_width == width)
+ return;
+
+ layout->screen_width = width;
+
+ gtk_text_layout_invalidate_all (layout);
+}
+
+void
+gtk_text_layout_get_size (GtkTextLayout *layout,
+ gint *width,
+ gint *height)
+{
+ gint w, h;
+
+ g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+
+ gtk_text_btree_get_view_size (layout->buffer->tree, layout,
+ &w, &h);
+
+ layout->width = w;
+ layout->height = h;
+
+ if (width)
+ *width = layout->width;
+
+ if (height)
+ *height = layout->height;
+}
+
+static void
+gtk_text_layout_invalidated (GtkTextLayout *layout)
+{
+ gtk_signal_emit (GTK_OBJECT (layout), signals[INVALIDATED]);
+}
+
+void
+gtk_text_layout_changed (GtkTextLayout *layout,
+ gint y,
+ gint old_height,
+ gint new_height)
+{
+ gtk_signal_emit (GTK_OBJECT (layout), signals[CHANGED], y, old_height, new_height);
+}
+
+void
+gtk_text_layout_free_line_data (GtkTextLayout *layout,
+ GtkTextLine *line,
+ GtkTextLineData *line_data)
+{
+ (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->free_line_data)
+ (layout, line, line_data);
+}
+
+void
+gtk_text_layout_invalidate (GtkTextLayout *layout,
+ const GtkTextIter *start_index,
+ const GtkTextIter *end_index)
+{
+ (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->invalidate)
+ (layout, start_index, end_index);
+}
+
+GtkTextLineData*
+gtk_text_layout_wrap (GtkTextLayout *layout,
+ GtkTextLine *line,
+ /* may be NULL */
+ GtkTextLineData *line_data)
+{
+ return (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->wrap) (layout, line, line_data);
+}
+
+void
+gtk_text_layout_get_log_attrs (GtkTextLayout *layout,
+ GtkTextLine *line,
+ PangoLogAttr **attrs,
+ gint *n_attrs)
+{
+ (* GTK_TEXT_LAYOUT_GET_CLASS (layout)->get_log_attrs)
+ (layout, line, attrs, n_attrs);
+}
+
+GSList*
+gtk_text_layout_get_lines (GtkTextLayout *layout,
+ /* [top_y, bottom_y) */
+ gint top_y,
+ gint bottom_y,
+ gint *first_line_y)
+{
+ GtkTextLine *first_btree_line;
+ GtkTextLine *last_btree_line;
+ GtkTextLine *line;
+ GSList *retval;
+
+ g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
+ g_return_val_if_fail (bottom_y > top_y, NULL);
+
+ retval = NULL;
+
+ first_btree_line = gtk_text_btree_find_line_by_y (layout->buffer->tree, layout, top_y, first_line_y);
+ if (first_btree_line == NULL)
+ {
+ g_assert (top_y > 0);
+ /* off the bottom */
+ return NULL;
+ }
+
+ /* -1 since bottom_y is one past */
+ last_btree_line = gtk_text_btree_find_line_by_y (layout->buffer->tree, layout, bottom_y - 1, NULL);
+
+ if (!last_btree_line)
+ last_btree_line = gtk_text_btree_get_line (layout->buffer->tree,
+ gtk_text_btree_line_count (layout->buffer->tree) - 1, NULL);
+
+ {
+ GtkTextLineData *ld = gtk_text_line_get_data (last_btree_line, layout);
+ if (ld->height == 0)
+ G_BREAKPOINT();
+ }
+
+ g_assert (last_btree_line != NULL);
+
+ line = first_btree_line;
+ while (TRUE)
+ {
+ retval = g_slist_prepend (retval, line);
+
+ if (line == last_btree_line)
+ break;
+
+ line = gtk_text_line_next (line);
+ }
+
+ retval = g_slist_reverse (retval);
+
+ return retval;
+}
+
+static void
+invalidate_cached_style (GtkTextLayout *layout)
+{
+ free_style_cache (layout);
+}
+
+/* These should be called around a loop which wraps a CONTIGUOUS bunch
+ * of display lines. If the lines aren't contiguous you can't call
+ * these.
+ */
+void
+gtk_text_layout_wrap_loop_start (GtkTextLayout *layout)
+{
+ g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+ g_return_if_fail (layout->one_style_cache == NULL);
+
+ layout->wrap_loop_count += 1;
+}
+
+void
+gtk_text_layout_wrap_loop_end (GtkTextLayout *layout)
+{
+ g_return_if_fail (layout->wrap_loop_count > 0);
+
+ layout->wrap_loop_count -= 1;
+
+ if (layout->wrap_loop_count == 0)
+ {
+ /* We cache a some stuff if we're iterating over some lines wrapping
+ * them. This cleans it up.
+ */
+ /* Nuke our cached style */
+ invalidate_cached_style (layout);
+ g_assert (layout->one_style_cache == NULL);
+ }
+}
+
+static void
+gtk_text_layout_invalidate_all (GtkTextLayout *layout)
+{
+ GtkTextIter start;
+ GtkTextIter end;
+
+ if (layout->buffer == NULL)
+ return;
+
+ gtk_text_buffer_get_bounds (layout->buffer, &start, &end);
+
+ gtk_text_layout_invalidate (layout, &start, &end);
+}
+
+/* FIXME: This is now completely generic, and we could probably be
+ * moved into gtktextbtree.c.
+ */
+static void
+gtk_text_layout_real_invalidate (GtkTextLayout *layout,
+ const GtkTextIter *start,
+ const GtkTextIter *end)
+{
+ GtkTextLine *line;
+ GtkTextLine *last_line;
+
+ g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+ g_return_if_fail (layout->wrap_loop_count == 0);
+
+#if 0
+ gtk_text_view_index_spew (start_index, "invalidate start");
+ gtk_text_view_index_spew (end_index, "invalidate end");
+#endif
+
+ last_line = gtk_text_iter_get_line (end);
+ line = gtk_text_iter_get_line (start);
+
+ while (TRUE)
+ {
+ GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
+
+ if (line_data &&
+ (line != last_line || !gtk_text_iter_starts_line (end)))
+ {
+ if (layout->one_display_cache &&
+ line == layout->one_display_cache->line)
+ {
+ GtkTextLineDisplay *tmp_display = layout->one_display_cache;
+ layout->one_display_cache = NULL;
+ gtk_text_layout_free_line_display (layout, tmp_display);
+ }
+ gtk_text_line_invalidate_wrap (line, line_data);
+ }
+
+ if (line == last_line)
+ break;
+
+ line = gtk_text_line_next (line);
+ }
+
+ gtk_text_layout_invalidated (layout);
+}
+
+static void
+gtk_text_layout_real_free_line_data (GtkTextLayout *layout,
+ GtkTextLine *line,
+ GtkTextLineData *line_data)
+{
+ if (layout->one_display_cache && line == layout->one_display_cache->line)
+ {
+ GtkTextLineDisplay *tmp_display = layout->one_display_cache;
+ layout->one_display_cache = NULL;
+ gtk_text_layout_free_line_display (layout, tmp_display);
+ }
+
+ g_free (line_data);
+}
+
+
+
+/**
+ * gtk_text_layout_is_valid:
+ * @layout: a #GtkTextLayout
+ *
+ * Check if there are any invalid regions in a #GtkTextLayout's buffer
+ *
+ * Return value: #TRUE if any invalid regions were found
+ **/
+gboolean
+gtk_text_layout_is_valid (GtkTextLayout *layout)
+{
+ g_return_val_if_fail (layout != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), FALSE);
+
+ return gtk_text_btree_is_valid (layout->buffer->tree, layout);
+}
+
+/**
+ * gtk_text_layout_validate_yrange:
+ * @layout: a #GtkTextLayout
+ * @anchor: iter pointing into a line that will be used as the
+ * coordinate origin
+ * @y0: offset from the top of the line pointed to by @anchor at
+ * which to begin validation. (The offset here is in pixels
+ * after validation.)
+ * @y1: offset from the top of the line pointed to by @anchor at
+ * which to end validation. (The offset here is in pixels
+ * after validation.)
+ *
+ * Ensure that a region of a #GtkTextLayout is valid. The ::changed
+ * signal will be emitted if any lines are validated.
+ **/
+void
+gtk_text_layout_validate_yrange (GtkTextLayout *layout,
+ GtkTextIter *anchor,
+ gint y0,
+ gint y1)
+{
+ GtkTextLine *line;
+ GtkTextLine *first_line = NULL;
+ GtkTextLine *last_line = NULL;
+ gint seen;
+ gint delta_height = 0;
+ gint first_line_y = 0; /* Quiet GCC */
+ gint last_line_y = 0; /* Quiet GCC */
+
+ g_return_if_fail (layout != NULL);
+ g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+
+ if (y0 > 0)
+ y0 = 0;
+ if (y1 < 0)
+ y1 = 0;
+
+ /* Validate backwards from the anchor line to y0
+ */
+ line = gtk_text_iter_get_line (anchor);
+ seen = 0;
+ while (line && seen < -y0)
+ {
+ GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
+ if (!line_data || !line_data->valid)
+ {
+ gint old_height = line_data ? line_data->height : 0;
+
+ gtk_text_btree_validate_line (layout->buffer->tree, line, layout);
+ line_data = gtk_text_line_get_data (line, layout);
+
+ delta_height += line_data->height - old_height;
+
+ first_line = line;
+ first_line_y = -seen;
+ if (!last_line)
+ {
+ last_line = line;
+ last_line_y = -seen + line_data->height;
+ }
+ }
+
+ seen += line_data->height;
+ line = gtk_text_line_previous (line);
+ }
+
+ /* Validate forwards to y1 */
+ line = gtk_text_iter_get_line (anchor);
+ seen = 0;
+ while (line && seen < y1)
+ {
+ GtkTextLineData *line_data = gtk_text_line_get_data (line, layout);
+ if (!line_data || !line_data->valid)
+ {
+ gint old_height = line_data ? line_data->height : 0;
+
+ gtk_text_btree_validate_line (layout->buffer->tree, line, layout);
+ line_data = gtk_text_line_get_data (line, layout);
+
+ delta_height += line_data->height - old_height;
+
+ if (!first_line)
+ {
+ first_line = line;
+ first_line_y = seen;
+ }
+ last_line = line;
+ last_line_y = seen + line_data->height;
+ }
+
+ seen += line_data->height;
+ line = gtk_text_line_next (line);
+ }
+
+ /* If we found and validated any invalid lines, emit the changed singal
+ */
+ if (first_line)
+ {
+ gint line_top = gtk_text_btree_find_line_top (layout->buffer->tree, first_line, layout);
+
+ gtk_text_layout_changed (layout,
+ line_top,
+ last_line_y - first_line_y - delta_height,
+ last_line_y - first_line_y);
+ }
+}
+
+/**
+ * gtk_text_layout_validate:
+ * @tree: a #GtkTextLayout
+ * @max_pixels: the maximum number of pixels to validate. (No more
+ * than one paragraph beyond this limit will be validated)
+ *
+ * Validate regions of a #GtkTextLayout. The ::changed signal will
+ * be emitted for each region validated.
+ **/
+void
+gtk_text_layout_validate (GtkTextLayout *layout,
+ gint max_pixels)
+{
+ gint y, old_height, new_height;
+
+ g_return_if_fail (layout != NULL);
+ g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+
+ while (max_pixels > 0 &&
+ gtk_text_btree_validate (layout->buffer->tree, layout, max_pixels,
+ &y, &old_height, &new_height))
+ {
+ max_pixels -= new_height;
+ gtk_text_layout_changed (layout, y, old_height, new_height);
+ }
+}
+
+static GtkTextLineData*
+gtk_text_layout_real_wrap (GtkTextLayout *layout,
+ GtkTextLine *line,
+ /* may be NULL */
+ GtkTextLineData *line_data)
+{
+ GtkTextLineDisplay *display;
+
+ g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
+
+ if (line_data == NULL)
+ {
+ line_data = gtk_text_line_data_new (layout, line);
+ gtk_text_line_add_data (line, line_data);
+ }
+
+ display = gtk_text_layout_get_line_display (layout, line, TRUE);
+ line_data->width = display->width;
+ line_data->height = display->height;
+ line_data->valid = TRUE;
+ gtk_text_layout_free_line_display (layout, display);
+
+ return line_data;
+}
+
+static void
+gtk_text_layout_real_get_log_attrs (GtkTextLayout *layout,
+ GtkTextLine *line,
+ PangoLogAttr **attrs,
+ gint *n_attrs)
+{
+ GtkTextLineDisplay *display;
+
+ g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), NULL);
+
+ display = gtk_text_layout_get_line_display (layout, line, TRUE);
+ pango_layout_get_log_attrs (display->layout, attrs, n_attrs);
+ gtk_text_layout_free_line_display (layout, display);
+}
+
+/*
+ * Layout utility functions
+ */
+
+/* If you get the style with get_style () you need to call
+ release_style () to free it. */
+static GtkTextStyleValues*
+get_style (GtkTextLayout *layout,
+ const GtkTextIter *iter)
+{
+ GtkTextTag** tags;
+ gint tag_count = 0;
+ GtkTextStyleValues *style;
+
+ /* If we have the one-style cache, then it means
+ that we haven't seen a toggle since we filled in the
+ one-style cache.
+ */
+ if (layout->one_style_cache != NULL)
+ {
+ gtk_text_view_style_values_ref (layout->one_style_cache);
+ return layout->one_style_cache;
+ }
+
+ g_assert (layout->one_style_cache == NULL);
+
+ /* Get the tags at this spot */
+ tags = gtk_text_btree_get_tags (iter, &tag_count);
+
+ /* No tags, use default style */
+ if (tags == NULL || tag_count == 0)
+ {
+ /* One ref for the return value, one ref for the
+ layout->one_style_cache reference */
+ gtk_text_view_style_values_ref (layout->default_style);
+ gtk_text_view_style_values_ref (layout->default_style);
+ layout->one_style_cache = layout->default_style;
+
+ if (tags)
+ g_free (tags);
+
+ return layout->default_style;
+ }
+
+ /* Sort tags in ascending order of priority */
+ gtk_text_tag_array_sort (tags, tag_count);
+
+ style = gtk_text_view_style_values_new ();
+
+ gtk_text_view_style_values_copy (layout->default_style,
+ style);
+
+ gtk_text_view_style_values_fill_from_tags (style,
+ tags,
+ tag_count);
+
+ g_free (tags);
+
+ g_assert (style->refcount == 1);
+
+ /* Leave this style as the last one seen */
+ g_assert (layout->one_style_cache == NULL);
+ gtk_text_view_style_values_ref (style); /* ref held by layout->one_style_cache */
+ layout->one_style_cache = style;
+
+ /* Returning yet another refcount */
+ return style;
+}
+
+static void
+release_style (GtkTextLayout *layout,
+ GtkTextStyleValues *style)
+{
+ g_return_if_fail (style != NULL);
+ g_return_if_fail (style->refcount > 0);
+
+ gtk_text_view_style_values_unref (style);
+}
+
+/*
+ * Lines
+ */
+
+/* This function tries to optimize the case where a line
+ is completely invisible */
+static gboolean
+totally_invisible_line (GtkTextLayout *layout,
+ GtkTextLine *line,
+ GtkTextIter *iter)
+{
+ GtkTextLineSegment *seg;
+ int bytes = 0;
+
+ /* If we have a cached style, then we know it does actually apply
+ and we can just see if it is elided. */
+ if (layout->one_style_cache &&
+ !layout->one_style_cache->elide)
+ return FALSE;
+ /* Without the cache, we check if the first char is visible, if so
+ we are partially visible. Note that we have to check this since
+ we don't know the current elided/nonelided toggle state; this
+ function can use the whole btree to get it right. */
+ else
+ {
+ gtk_text_btree_get_iter_at_line(layout->buffer->tree, iter, line, 0);
+
+ if (!gtk_text_btree_char_is_invisible (iter))
+ return FALSE;
+ }
+
+ bytes = 0;
+ seg = line->segments;
+
+ while (seg != NULL)
+ {
+ if (seg->byte_count > 0)
+ bytes += seg->byte_count;
+
+ /* Note that these two tests can cause us to bail out
+ when we shouldn't, because a higher-priority tag
+ may override these settings. However the important
+ thing is to only elide really-elided lines, rather
+ than to elide all really-elided lines. */
+
+ else if (seg->type == &gtk_text_toggle_on_type)
+ {
+ invalidate_cached_style (layout);
+
+ /* Bail out if an elision-unsetting tag begins */
+ if (seg->body.toggle.info->tag->elide_set &&
+ !seg->body.toggle.info->tag->values->elide)
+ break;
+ }
+ else if (seg->type == &gtk_text_toggle_off_type)
+ {
+ invalidate_cached_style (layout);
+
+ /* Bail out if an elision-setting tag ends */
+ if (seg->body.toggle.info->tag->elide_set &&
+ seg->body.toggle.info->tag->values->elide)
+ break;
+ }
+
+ seg = seg->next;
+ }
+
+ if (seg != NULL) /* didn't reach line end */
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+set_para_values (GtkTextLayout *layout,
+ GtkTextStyleValues *style,
+ GtkTextLineDisplay *display,
+ gdouble *align)
+{
+ PangoAlignment pango_align = PANGO_ALIGN_LEFT;
+ int layout_width;
+
+ display->direction = style->direction;
+
+ if (display->direction == GTK_TEXT_DIR_LTR)
+ display->layout = pango_layout_new (layout->ltr_context);
+ else
+ display->layout = pango_layout_new (layout->rtl_context);
+
+ switch (style->justify)
+ {
+ case GTK_JUSTIFY_LEFT:
+ pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_LEFT : PANGO_ALIGN_RIGHT;
+ break;
+ case GTK_JUSTIFY_RIGHT:
+ pango_align = (style->direction == GTK_TEXT_DIR_LTR) ? PANGO_ALIGN_RIGHT : PANGO_ALIGN_LEFT;
+ break;
+ case GTK_JUSTIFY_CENTER:
+ pango_align = PANGO_ALIGN_CENTER;
+ break;
+ case GTK_JUSTIFY_FILL:
+ g_warning ("FIXME we don't support GTK_JUSTIFY_FILL yet");
+ break;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ switch (pango_align)
+ {
+ case PANGO_ALIGN_LEFT:
+ *align = 0.0;
+ break;
+ case PANGO_ALIGN_RIGHT:
+ *align = 1.0;
+ break;
+ case PANGO_ALIGN_CENTER:
+ *align = 0.5;
+ break;
+ }
+
+ pango_layout_set_alignment (display->layout, pango_align);
+ pango_layout_set_spacing (display->layout, style->pixels_inside_wrap * PANGO_SCALE);
+
+ display->top_margin = style->pixels_above_lines;
+ display->height = style->pixels_above_lines + style->pixels_below_lines;
+ display->bottom_margin = style->pixels_below_lines;
+ display->x_offset = display->left_margin = MIN (style->left_margin, style->left_wrapped_line_margin);
+ display->right_margin = style->right_margin;
+
+ pango_layout_set_indent (display->layout, style->left_margin - style->left_wrapped_line_margin);
+
+ switch (style->wrap_mode)
+ {
+ case GTK_WRAPMODE_CHAR:
+ /* FIXME: Handle this; for now, fall-through */
+ case GTK_WRAPMODE_WORD:
+ display->total_width = -1;
+ layout_width = layout->screen_width - display->x_offset - style->right_margin;
+ pango_layout_set_width (display->layout, layout_width * PANGO_SCALE);
+ break;
+ case GTK_WRAPMODE_NONE:
+ display->total_width = MAX (layout->screen_width, layout->width) - display->x_offset - style->right_margin;
+ break;
+ }
+}
+
+static PangoAttribute *
+gtk_text_attr_appearance_copy (const PangoAttribute *attr)
+{
+ const GtkTextAttrAppearance *appearance_attr = (const GtkTextAttrAppearance *)attr;
+
+ return gtk_text_attr_appearance_new (&appearance_attr->appearance);
+}
+
+static void
+gtk_text_attr_appearance_destroy (PangoAttribute *attr)
+{
+ GtkTextAppearance *appearance = &((GtkTextAttrAppearance *)attr)->appearance;
+
+ if (appearance->bg_stipple)
+ gdk_drawable_unref (appearance->bg_stipple);
+ if (appearance->fg_stipple)
+ gdk_drawable_unref (appearance->fg_stipple);
+
+ g_free (attr);
+}
+
+static gboolean
+gtk_text_attr_appearance_compare (const PangoAttribute *attr1,
+ const PangoAttribute *attr2)
+{
+ const GtkTextAppearance *appearance1 = &((const GtkTextAttrAppearance *)attr1)->appearance;
+ const GtkTextAppearance *appearance2 = &((const GtkTextAttrAppearance *)attr2)->appearance;
+
+ return (gdk_color_equal (&appearance1->fg_color, &appearance2->fg_color) &&
+ gdk_color_equal (&appearance1->bg_color, &appearance2->bg_color) &&
+ appearance1->fg_stipple == appearance2->fg_stipple &&
+ appearance1->bg_stipple == appearance2->bg_stipple &&
+ appearance1->underline == appearance2->underline &&
+ appearance1->overstrike == appearance2->overstrike &&
+ appearance1->draw_bg == appearance2->draw_bg);
+
+}
+
+/**
+ * gtk_text_attr_appearance_new:
+ * @desc:
+ *
+ * Create a new font description attribute. (This attribute
+ * allows setting family, style, weight, variant, stretch,
+ * and size simultaneously.)
+ *
+ * Return value:
+ **/
+static PangoAttribute *
+gtk_text_attr_appearance_new (const GtkTextAppearance *appearance)
+{
+ static PangoAttrClass klass = {
+ 0,
+ gtk_text_attr_appearance_copy,
+ gtk_text_attr_appearance_destroy,
+ gtk_text_attr_appearance_compare
+ };
+
+ GtkTextAttrAppearance *result;
+
+ if (!klass.type)
+ klass.type = gtk_text_attr_appearance_type =
+ pango_attr_type_register ("GtkTextAttrAppearance");
+
+ result = g_new (GtkTextAttrAppearance, 1);
+ result->attr.klass = &klass;
+
+ result->appearance = *appearance;
+
+ if (appearance->bg_stipple)
+ gdk_drawable_ref (appearance->bg_stipple);
+ if (appearance->fg_stipple)
+ gdk_drawable_ref (appearance->fg_stipple);
+
+ return (PangoAttribute *)result;
+}
+
+static void
+add_text_attrs (GtkTextLayout *layout,
+ GtkTextStyleValues *style,
+ gint byte_count,
+ PangoAttrList *attrs,
+ gint start,
+ gboolean size_only)
+{
+ PangoAttribute *attr;
+
+ attr = pango_attr_font_desc_new (style->font_desc);
+ attr->start_index = start;
+ attr->end_index = start + byte_count;
+
+ pango_attr_list_insert (attrs, attr);
+
+ if (!size_only)
+ {
+ attr = gtk_text_attr_appearance_new (&style->appearance);
+
+ attr->start_index = start;
+ attr->end_index = start + byte_count;
+
+ pango_attr_list_insert (attrs, attr);
+ }
+}
+
+static void
+add_pixmap_attrs (GtkTextLayout *layout,
+ GtkTextStyleValues *style,
+ GtkTextLineSegment *seg,
+ PangoAttrList *attrs,
+ gint start)
+{
+}
+
+static void
+add_cursor (GtkTextLayout *layout,
+ GtkTextLineDisplay *display,
+ GtkTextLineSegment *seg,
+ gint start)
+{
+ GtkTextIter selection_start, selection_end;
+
+ PangoRectangle strong_pos, weak_pos;
+ GtkTextCursorDisplay *cursor;
+
+ /* Hide insertion cursor when we have a selection
+ */
+ if (gtk_text_btree_mark_is_insert (layout->buffer->tree, seg) &&
+ gtk_text_buffer_get_selection_bounds (layout->buffer, &selection_start, &selection_end))
+ return;
+
+ pango_layout_get_cursor_pos (display->layout, start, &strong_pos, &weak_pos);
+
+ cursor = g_new (GtkTextCursorDisplay, 1);
+
+ cursor->x = strong_pos.x / PANGO_SCALE;
+ cursor->y = strong_pos.y / PANGO_SCALE;
+ cursor->height = strong_pos.height / PANGO_SCALE;
+ cursor->is_strong = TRUE;
+ display->cursors = g_slist_prepend (display->cursors, cursor);
+
+ if (weak_pos.x == strong_pos.x)
+ cursor->is_weak = TRUE;
+ else
+ {
+ cursor->is_weak = FALSE;
+
+ cursor = g_new (GtkTextCursorDisplay, 1);
+
+ cursor->x = weak_pos.x / PANGO_SCALE;
+ cursor->y = weak_pos.y / PANGO_SCALE;
+ cursor->height = weak_pos.height / PANGO_SCALE;
+ cursor->is_strong = FALSE;
+ cursor->is_weak = TRUE;
+ display->cursors = g_slist_prepend (display->cursors, cursor);
+ }
+}
+
+GtkTextLineDisplay *
+gtk_text_layout_get_line_display (GtkTextLayout *layout,
+ GtkTextLine *line,
+ gboolean size_only)
+{
+ GtkTextLineDisplay *display;
+ GtkTextLineSegment *seg;
+ GtkTextIter iter;
+ GtkTextStyleValues *style;
+ gchar *text;
+ PangoAttrList *attrs;
+ gint byte_count, byte_offset;
+ gdouble align;
+ PangoRectangle extents;
+ gboolean para_values_set = FALSE;
+ GSList *cursor_byte_offsets = NULL;
+ GSList *cursor_segs = NULL;
+ GSList *tmp_list1, *tmp_list2;
+
+ g_return_val_if_fail (line != NULL, NULL);
+
+ if (layout->one_display_cache)
+ {
+ if (line == layout->one_display_cache->line &&
+ (size_only || !layout->one_display_cache->size_only))
+ return layout->one_display_cache;
+ else
+ {
+ GtkTextLineDisplay *tmp_display = layout->one_display_cache;
+ layout->one_display_cache = NULL;
+ gtk_text_layout_free_line_display (layout, tmp_display);
+ }
+ }
+
+ display = g_new0 (GtkTextLineDisplay, 1);
+
+ display->size_only = size_only;
+ display->line = line;
+
+ gtk_text_btree_get_iter_at_line (layout->buffer->tree, &iter, line, 0);
+
+ /* Special-case optimization for completely
+ * invisible lines; makes it faster to deal
+ * with sequences of invisible lines.
+ */
+ if (totally_invisible_line (layout, line, &iter))
+ return display;
+
+ /* Allocate space for flat text for buffer
+ */
+ byte_count = gtk_text_line_byte_count (line);
+ text = g_malloc (byte_count);
+
+ attrs = pango_attr_list_new ();
+
+ /* Iterate over segments, creating display chunks for them. */
+ byte_offset = 0;
+ seg = gtk_text_iter_get_any_segment (&iter);
+ while (seg != NULL)
+ {
+ /* Displayable segments */
+ if (seg->type == &gtk_text_char_type ||
+ seg->type == &gtk_text_pixmap_type)
+ {
+ gtk_text_btree_get_iter_at_line (layout->buffer->tree,
+ &iter, line,
+ byte_offset);
+ style = get_style (layout, &iter);
+
+ /* We have to delay setting the paragraph values until we
+ * hit the first pixmap or text segment because toggles at
+ * the beginning of the paragraph should affect the
+ * paragraph-global values
+ */
+ if (!para_values_set)
+ {
+ set_para_values (layout, style, display, &align);
+ para_values_set = TRUE;
+ }
+
+ /* First see if the chunk is elided, and ignore it if so. Tk
+ * looked at tabs, wrap mode, etc. before doing this, but
+ * that made no sense to me, so I am just skipping the
+ * elided chunks
+ */
+ if (!style->elide)
+ {
+ if (seg->type == &gtk_text_char_type)
+ {
+ /* We don't want to split segments because of marks, so we scan forward
+ * for more segments only separated from us by marks. In theory, we
+ * should also merge segments with identical styles, even if there
+ * are toggles in-between
+ */
+
+ gint byte_count = 0;
+
+ while (TRUE)
+ {
+ if (seg->type == &gtk_text_char_type)
+ {
+ memcpy (text + byte_offset, seg->body.chars, seg->byte_count);
+ byte_offset += seg->byte_count;
+ byte_count += seg->byte_count;
+ }
+ else if (seg->body.mark.visible)
+ {
+ cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
+ cursor_segs = g_slist_prepend (cursor_segs, seg);
+ }
+
+ if (!seg->next ||
+ (seg->next->type != &gtk_text_right_mark_type &&
+ seg->next->type != &gtk_text_left_mark_type &&
+ seg->next->type != &gtk_text_char_type))
+ break;
+
+ seg = seg->next;
+ }
+
+ add_text_attrs (layout, style, byte_count, attrs, byte_offset - byte_count, size_only);
+ }
+ else
+ {
+ add_pixmap_attrs (layout, style, seg, attrs, byte_offset);
+ memcpy (text + byte_offset, gtk_text_unknown_char_utf8, seg->byte_count);
+ byte_offset += seg->byte_count;
+ }
+ }
+
+ release_style (layout, style);
+ }
+
+ /* Toggles */
+ else if (seg->type == &gtk_text_toggle_on_type ||
+ seg->type == &gtk_text_toggle_off_type)
+ {
+ /* Style may have changed, drop our
+ current cached style */
+ invalidate_cached_style (layout);
+ }
+
+ /* Marks */
+ else if (seg->type == &gtk_text_right_mark_type ||
+ seg->type == &gtk_text_left_mark_type)
+ {
+ /* Display visible marks */
+
+ if (seg->body.mark.visible)
+ {
+ cursor_byte_offsets = g_slist_prepend (cursor_byte_offsets, GINT_TO_POINTER (byte_offset));
+ cursor_segs = g_slist_prepend (cursor_segs, seg);
+ }
+ }
+
+ else
+ g_error ("Unknown segment type: %s", seg->type->name);
+
+ seg = seg->next;
+ }
+
+ if (!para_values_set)
+ {
+ style = get_style (layout, &iter);
+ set_para_values (layout, style, display, &align);
+ release_style (layout, style);
+ }
+
+ /* Pango doesn't want the trailing new line */
+ if (byte_offset > 0 && text[byte_offset - 1] == '\n')
+ byte_offset--;
+
+ pango_layout_set_text (display->layout, text, byte_offset);
+ pango_layout_set_attributes (display->layout, attrs);
+
+ tmp_list1 = cursor_byte_offsets;
+ tmp_list2 = cursor_segs;
+ while (tmp_list1)
+ {
+ add_cursor (layout, display, tmp_list2->data, GPOINTER_TO_INT (tmp_list1->data));
+ tmp_list1 = tmp_list1->next;
+ tmp_list2 = tmp_list2->next;
+ }
+ g_slist_free (cursor_byte_offsets);
+ g_slist_free (cursor_segs);
+
+ pango_layout_get_extents (display->layout, NULL, &extents);
+
+ if (display->total_width >= 0)
+ display->x_offset += (display->total_width - extents.width / PANGO_SCALE) * align;
+
+ display->width = extents.width / PANGO_SCALE + display->left_margin + display->right_margin;
+ display->height += extents.height / PANGO_SCALE;
+
+ /* Free this if we aren't in a loop */
+ if (layout->wrap_loop_count == 0)
+ invalidate_cached_style (layout);
+
+ g_free (text);
+ pango_attr_list_unref (attrs);
+
+ layout->one_display_cache = display;
+
+ return display;
+}
+
+void
+gtk_text_layout_free_line_display (GtkTextLayout *layout,
+ GtkTextLineDisplay *display)
+{
+ if (display != layout->one_display_cache)
+ {
+ pango_layout_unref (display->layout);
+
+ if (display->cursors)
+ {
+ g_slist_foreach (display->cursors, (GFunc)g_free, NULL);
+ g_slist_free (display->cursors);
+ }
+
+ g_free (display);
+ }
+}
+
+/* FIXME: This really doesn't belong in this file ... */
+static GtkTextLineData*
+gtk_text_line_data_new (GtkTextLayout *layout,
+ GtkTextLine *line)
+{
+ GtkTextLineData *line_data;
+
+ line_data = g_new (GtkTextLineData, 1);
+
+ line_data->view_id = layout;
+ line_data->next = NULL;
+ line_data->width = 0;
+ line_data->height = 0;
+ line_data->valid = FALSE;
+
+ return line_data;
+}
+
+static void
+get_line_at_y (GtkTextLayout *layout,
+ gint y,
+ GtkTextLine **line,
+ gint *line_top)
+{
+ if (y < 0)
+ y = 0;
+ if (y > layout->height)
+ y = layout->height;
+
+ *line = gtk_text_btree_find_line_by_y (layout->buffer->tree, layout, y, line_top);
+ if (*line == NULL)
+ {
+ *line = gtk_text_btree_get_line (layout->buffer->tree,
+ gtk_text_btree_line_count (layout->buffer->tree) - 1, NULL);
+ if (line_top)
+ *line_top = gtk_text_btree_find_line_top (layout->buffer->tree, *line, layout);
+ }
+}
+
+/**
+ * gtk_text_layout_get_line_at_y:
+ * @layout: a #GtkLayout
+ * @target_iter: the iterator in which the result is stored
+ * @y: the y positition
+ * @line_top: location to store the y coordinate of the
+ * top of the line. (Can by %NULL.)
+ *
+ * Get the iter at the beginning of the line which is displayed
+ * at the given y.
+ **/
+void
+gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
+ GtkTextIter *target_iter,
+ gint y,
+ gint *line_top)
+{
+ GtkTextLine *line;
+
+ g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+ g_return_if_fail (target_iter != NULL);
+
+ get_line_at_y (layout, y, &line, line_top);
+ gtk_text_btree_get_iter_at_line (layout->buffer->tree, target_iter, line, 0);
+}
+
+void
+gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
+ GtkTextIter *target_iter,
+ gint x, gint y)
+{
+ GtkTextLine *line;
+ gint byte_index, trailing;
+ gint line_top;
+ GtkTextLineDisplay *display;
+
+ g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+ g_return_if_fail (target_iter != NULL);
+
+ /* Adjust pixels to be on-screen. This gives nice
+ behavior if the user is dragging with a pointer grab.
+ */
+ if (x < 0)
+ x = 0;
+ if (x > layout->width)
+ x = layout->width;
+
+ get_line_at_y (layout, y, &line, &line_top);
+
+ display = gtk_text_layout_get_line_display (layout, line, TRUE);
+
+ x -= display->x_offset;
+ y -= line_top + display->top_margin;
+
+ /* We clamp y to the area of the actual layout so that the layouts
+ * hit testing works OK on the space above and below the layout
+ */
+ y = CLAMP (y, display->top_margin, display->height - display->top_margin - display->bottom_margin - 1);
+
+ if (!pango_layout_xy_to_index (display->layout, x * PANGO_SCALE, y * PANGO_SCALE,
+ &byte_index, &trailing))
+ {
+ byte_index = gtk_text_line_byte_count (line);
+ trailing = 0;
+ }
+
+ gtk_text_btree_get_iter_at_line (layout->buffer->tree,
+ target_iter,
+ line, byte_index);
+
+ while (trailing--)
+ gtk_text_iter_forward_char (target_iter);
+
+ gtk_text_layout_free_line_display (layout, display);
+}
+
+/**
+ * gtk_text_layout_get_cursor_locations
+ * @layout: a #GtkTextLayout
+ * @iter: a #GtkTextIter
+ * @strong_pos: location to store the strong cursor position (may be %NULL)
+ * @weak_pos: location to store the weak cursor position (may be %NULL)
+ *
+ * Given an iterator within a text laout, determine the positions that of the
+ * strong and weak cursors if the insertion point is at that
+ * iterator. The position of each cursor is stored as a zero-width
+ * rectangle. The strong cursor location is the location where
+ * characters of the directionality equal to the base direction of the
+ * paragraph are inserted. The weak cursor location is the location
+ * where characters of the directionality opposite to the base
+ * direction of the paragraph are inserted.
+ **/
+void
+gtk_text_layout_get_cursor_locations (GtkTextLayout *layout,
+ GtkTextIter *iter,
+ GdkRectangle *strong_pos,
+ GdkRectangle *weak_pos)
+{
+ GtkTextLine *line;
+ GtkTextLineDisplay *display;
+ gint line_top;
+
+ PangoRectangle pango_strong_pos;
+ PangoRectangle pango_weak_pos;
+
+ g_return_if_fail (layout != NULL);
+ g_return_if_fail (iter != NULL);
+
+ line = gtk_text_iter_get_line (iter);
+ line_top = gtk_text_btree_find_line_top (layout->buffer->tree, line, layout);
+
+ display = gtk_text_layout_get_line_display (layout, line, TRUE);
+
+ pango_layout_get_cursor_pos (display->layout, gtk_text_iter_get_line_byte (iter),
+ strong_pos ? &pango_strong_pos : NULL,
+ weak_pos ? &pango_weak_pos : NULL);
+
+ if (strong_pos)
+ {
+ strong_pos->x = display->x_offset + pango_strong_pos.x / PANGO_SCALE;
+ strong_pos->y = line_top + display->top_margin + pango_strong_pos.y / PANGO_SCALE;
+ strong_pos->width = 0;
+ strong_pos->height = pango_strong_pos.height / PANGO_SCALE;
+ }
+
+ if (weak_pos)
+ {
+ weak_pos->x = display->x_offset + pango_weak_pos.x / PANGO_SCALE;
+ weak_pos->y = line_top + display->top_margin + pango_weak_pos.y / PANGO_SCALE;
+ weak_pos->width = 0;
+ weak_pos->height = pango_weak_pos.height / PANGO_SCALE;
+ }
+
+ gtk_text_layout_free_line_display (layout, display);
+}
+
+/**
+ * gtk_text_layout_get_line_y:
+ * @layout: a #GtkTextLayout
+ * @iter: a #GtkTextIter
+ *
+ * Find the y coordinate of the top of the paragraph containing
+ * the given iter.
+ *
+ * Return value: the y coordinate, in pixels.
+ **/
+gint
+gtk_text_layout_get_line_y (GtkTextLayout *layout,
+ const GtkTextIter *iter)
+{
+ GtkTextLine *line;
+
+ g_return_val_if_fail (GTK_IS_TEXT_LAYOUT (layout), 0);
+ g_return_val_if_fail (gtk_text_iter_get_btree (iter) == layout->buffer->tree, 0);
+
+ line = gtk_text_iter_get_line (iter);
+ return gtk_text_btree_find_line_top (layout->buffer->tree, line, layout);
+}
+
+void
+gtk_text_layout_get_iter_location (GtkTextLayout *layout,
+ const GtkTextIter *iter,
+ GdkRectangle *rect)
+{
+ PangoRectangle pango_rect;
+ GtkTextLine *line;
+ GtkTextBTree *tree;
+ GtkTextLineDisplay *display;
+ gint byte_index;
+
+ g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+ g_return_if_fail (gtk_text_iter_get_btree (iter) == layout->buffer->tree);
+ g_return_if_fail (rect != NULL);
+
+ tree = gtk_text_iter_get_btree (iter);
+ line = gtk_text_iter_get_line (iter);
+
+ display = gtk_text_layout_get_line_display (layout, line, TRUE);
+
+ rect->y = gtk_text_btree_find_line_top (tree, line, layout);
+
+ /* pango_layout_index_to_pos() expects the index of a character within the layout,
+ * so we have to special case the last character. FIXME: This should be moved
+ * to Pango.
+ */
+ if (gtk_text_iter_ends_line (iter))
+ {
+ PangoLayoutLine *last_line = g_slist_last (pango_layout_get_lines (display->layout))->data;
+
+ pango_layout_line_get_extents (last_line, NULL, &pango_rect);
+
+ rect->x = display->x_offset + (pango_rect.x + pango_rect.width) / PANGO_SCALE;
+ rect->y += display->top_margin;
+ rect->width = 0;
+ rect->height = pango_rect.height / PANGO_SCALE;
+ }
+ else
+ {
+ byte_index = gtk_text_iter_get_line_byte (iter);
+
+ pango_layout_index_to_pos (display->layout, byte_index, &pango_rect);
+
+ rect->x = display->x_offset + pango_rect.x / PANGO_SCALE;
+ rect->y += display->top_margin;
+ rect->width = pango_rect.width / PANGO_SCALE;
+ rect->height = pango_rect.height / PANGO_SCALE;
+ }
+
+ gtk_text_layout_free_line_display (layout, display);
+}
+
+/* Find the iter for the logical beginning of the first display line whose
+ * top y is >= y. If none exists, move the iter to the logical beginning
+ * of the last line in the buffer.
+ */
+static void
+find_display_line_below (GtkTextLayout *layout,
+ GtkTextIter *iter,
+ gint y)
+{
+ GtkTextLine *line, *next;
+ GtkTextLine *found_line = NULL;
+ gint line_top;
+ gint found_byte = 0;
+
+ line = gtk_text_btree_find_line_by_y (layout->buffer->tree, layout, y, &line_top);
+ if (!line)
+ {
+ line = gtk_text_btree_get_line (layout->buffer->tree,
+ gtk_text_btree_line_count (layout->buffer->tree) - 1, NULL);
+ line_top = gtk_text_btree_find_line_top (layout->buffer->tree, line, layout);
+ }
+
+ while (line && !found_line)
+ {
+ GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, TRUE);
+ gint byte_index = 0;
+ GSList *tmp_list = pango_layout_get_lines (display->layout);
+
+ line_top += display->top_margin;
+
+ while (tmp_list)
+ {
+ PangoRectangle logical_rect;
+ PangoLayoutLine *layout_line = tmp_list->data;
+
+ found_byte = byte_index;
+
+ if (line_top >= y)
+ {
+ found_line = line;
+ break;
+ }
+
+ pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
+ line_top += logical_rect.height / PANGO_SCALE;
+
+ tmp_list = tmp_list->next;
+ }
+
+ line_top += display->bottom_margin;
+ gtk_text_layout_free_line_display (layout, display);
+
+ next = gtk_text_line_next (line);
+ if (!next)
+ found_line = line;
+
+ line = next;
+ }
+
+ gtk_text_btree_get_iter_at_line (layout->buffer->tree, iter, found_line, found_byte);
+}
+
+/* Find the iter for the logical beginning of the last display line whose
+ * top y is >= y. If none exists, move the iter to the logical beginning
+ * of the first line in the buffer.
+ */
+static void
+find_display_line_above (GtkTextLayout *layout,
+ GtkTextIter *iter,
+ gint y)
+{
+ GtkTextLine *line;
+ GtkTextLine *found_line = NULL;
+ gint line_top;
+ gint found_byte = 0;
+
+ line = gtk_text_btree_find_line_by_y (layout->buffer->tree, layout, y, &line_top);
+ if (!line)
+ {
+ line = gtk_text_btree_get_line (layout->buffer->tree,
+ gtk_text_btree_line_count (layout->buffer->tree) - 1, NULL);
+ line_top = gtk_text_btree_find_line_top (layout->buffer->tree, line, layout);
+ }
+
+ while (line && !found_line)
+ {
+ GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, TRUE);
+ PangoRectangle logical_rect;
+
+ gint byte_index = 0;
+ GSList *tmp_list;
+ gint tmp_top;
+
+ line_top -= display->top_margin + display->bottom_margin;
+ pango_layout_get_extents (display->layout, NULL, &logical_rect);
+ line_top -= logical_rect.height / PANGO_SCALE;
+
+ tmp_top = line_top + display->top_margin;
+
+ tmp_list = pango_layout_get_lines (display->layout);
+ while (tmp_list)
+ {
+ PangoLayoutLine *layout_line = tmp_list->data;
+
+ found_byte = byte_index;
+
+ tmp_top += logical_rect.height / PANGO_SCALE;
+
+ if (tmp_top < y)
+ {
+ found_line = line;
+ found_byte = byte_index;
+ }
+
+ pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
+
+ tmp_list = tmp_list->next;
+ }
+
+ gtk_text_layout_free_line_display (layout, display);
+
+ line = gtk_text_line_previous (line);
+ }
+
+ if (found_line)
+ gtk_text_btree_get_iter_at_line (layout->buffer->tree, iter, found_line, found_byte);
+ else
+ gtk_text_buffer_get_iter_at_char (layout->buffer, iter, 0);
+}
+
+/**
+ * gtk_text_layout_clamp_iter_to_vrange:
+ * @layout: a #GtkTextLayout
+ * @iter: a #GtkTextIter
+ * @top: the top of the range
+ * @bottom: the bottom the range
+ *
+ * If the iterator is not fully in the range @top <= y < @bottom,
+ * then, if possible, move it the minimum distance so that the
+ * iterator in this range.
+ *
+ * Returns: %TRUE if the iterator was moved, otherwise %FALSE.
+ **/
+gboolean
+gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
+ GtkTextIter *iter,
+ gint top,
+ gint bottom)
+{
+ GdkRectangle iter_rect;
+
+ gtk_text_layout_get_iter_location (layout, iter, &iter_rect);
+
+ /* If the iter is at least partially above the range, put the iter
+ * at the first fully visible line after the range.
+ */
+ if (iter_rect.y < top)
+ {
+ find_display_line_below (layout, iter, top);
+
+ return TRUE;
+ }
+ /* Otherwise, if the iter is at least partially below the screen, put the
+ * iter on the last logical position of the last completely visible
+ * line on screen
+ */
+ else if (iter_rect.y + iter_rect.height > bottom)
+ {
+ find_display_line_above (layout, iter, bottom);
+
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+/**
+ * gtk_text_layout_move_iter_to_next_line:
+ * @layout: a #GtkLayout
+ * @iter: a #GtkTextIter
+ *
+ * Move the iterator to the beginning of the previous line. The lines
+ * of a wrapped paragraph are treated as distinct for this operation.
+ **/
+void
+gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
+ GtkTextIter *iter)
+{
+ GtkTextLine *line;
+ GtkTextLineDisplay *display;
+ gint line_byte;
+ GSList *tmp_list;
+ PangoLayoutLine *layout_line;
+
+ g_return_if_fail (layout != NULL);
+ g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+ g_return_if_fail (iter != NULL);
+
+ line = gtk_text_iter_get_line (iter);
+ line_byte = gtk_text_iter_get_line_byte (iter);
+
+ display = gtk_text_layout_get_line_display (layout, line, TRUE);
+
+ tmp_list = pango_layout_get_lines (display->layout);
+ layout_line = tmp_list->data;
+
+ if (line_byte < layout_line->length || !tmp_list->next) /* first line of paragraph */
+ {
+ GtkTextLine *prev_line = gtk_text_line_previous (line);
+
+ if (prev_line)
+ {
+ gint byte_offset = 0;
+
+ gtk_text_layout_free_line_display (layout, display);
+ display = gtk_text_layout_get_line_display (layout, prev_line, TRUE);
+
+ tmp_list = pango_layout_get_lines (display->layout);
+
+ while (tmp_list->next)
+ {
+ layout_line = tmp_list->data;
+ tmp_list = tmp_list->next;
+
+ byte_offset += layout_line->length;
+ }
+
+ gtk_text_btree_get_iter_at_line (layout->buffer->tree,
+ iter, prev_line, byte_offset);
+ }
+ else
+ gtk_text_btree_get_iter_at_line (layout->buffer->tree,
+ iter, line, 0);
+ }
+ else
+ {
+ gint prev_offset = 0;
+ gint byte_offset = layout_line->length;
+
+ tmp_list = tmp_list->next;
+ while (tmp_list)
+ {
+ layout_line = tmp_list->data;
+
+ if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
+ {
+ gtk_text_btree_get_iter_at_line (layout->buffer->tree,
+ iter, line, prev_offset);
+ break;
+ }
+
+ prev_offset = byte_offset;
+ byte_offset += layout_line->length;
+ tmp_list = tmp_list->next;
+ }
+ }
+
+ gtk_text_layout_free_line_display (layout, display);
+}
+
+/**
+ * gtk_text_layout_move_iter_to_next_line:
+ * @layout: a #GtkLayout
+ * @iter: a #GtkTextIter
+ *
+ * Move the iterator to the beginning of the next line. The
+ * lines of a wrapped paragraph are treated as distinct for
+ * this operation.
+ **/
+void
+gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
+ GtkTextIter *iter)
+{
+ GtkTextLine *line;
+ GtkTextLineDisplay *display;
+ gint line_byte;
+
+ gboolean found = FALSE;
+ gboolean found_after = FALSE;
+
+ g_return_if_fail (layout != NULL);
+ g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+ g_return_if_fail (iter != NULL);
+
+ line = gtk_text_iter_get_line (iter);
+ line_byte = gtk_text_iter_get_line_byte (iter);
+
+ while (line && !found_after)
+ {
+ gint byte_offset = 0;
+ GSList *tmp_list;
+
+ display = gtk_text_layout_get_line_display (layout, line, TRUE);
+
+ tmp_list = pango_layout_get_lines (display->layout);
+ while (tmp_list && !found_after)
+ {
+ PangoLayoutLine *layout_line = tmp_list->data;
+
+ if (found)
+ {
+ gtk_text_btree_get_iter_at_line (layout->buffer->tree,
+ iter, line,
+ byte_offset);
+ found_after = TRUE;
+ }
+ else if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
+ found = TRUE;
+
+ byte_offset += layout_line->length;
+ tmp_list = tmp_list->next;
+ }
+
+ gtk_text_layout_free_line_display (layout, display);
+
+ line = gtk_text_line_next (line);
+ }
+}
+
+/**
+ * gtk_text_layout_move_iter_to_x:
+ * @layout: a #GtkTextLayout
+ * @iter: a #GtkTextIter
+ * @x: X coordinate
+ *
+ * Keeping the iterator on the same line of the layout, move it to the
+ * specified X coordinate. The lines of a wrapped paragraph are
+ * treated as distinct for this operation.
+ **/
+void
+gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
+ GtkTextIter *iter,
+ gint x)
+{
+ GtkTextLine *line;
+ GtkTextLineDisplay *display;
+ gint line_byte;
+ gint byte_offset = 0;
+ GSList *tmp_list;
+
+ g_return_if_fail (layout != NULL);
+ g_return_if_fail (GTK_IS_TEXT_LAYOUT (layout));
+ g_return_if_fail (iter != NULL);
+
+ line = gtk_text_iter_get_line (iter);
+ line_byte = gtk_text_iter_get_line_byte (iter);
+
+ display = gtk_text_layout_get_line_display (layout, line, TRUE);
+
+ tmp_list = pango_layout_get_lines (display->layout);
+ while (tmp_list)
+ {
+ PangoLayoutLine *layout_line = tmp_list->data;
+
+ if (line_byte < byte_offset + layout_line->length || !tmp_list->next)
+ {
+ PangoRectangle logical_rect;
+ gint byte_index, trailing;
+ gint align = pango_layout_get_alignment (display->layout);
+ gint x_offset = display->left_margin * PANGO_SCALE;
+ gint width = pango_layout_get_width (display->layout);
+
+ if (width < 0)
+ width = display->total_width * PANGO_SCALE;
+
+ pango_layout_line_get_extents (layout_line, NULL, &logical_rect);
+
+ switch (align)
+ {
+ case PANGO_ALIGN_RIGHT:
+ x_offset += width - logical_rect.width;
+ break;
+ case PANGO_ALIGN_CENTER:
+ x_offset += (width - logical_rect.width) / 2;
+ break;
+ default:
+ break;
+ }
+
+ pango_layout_line_x_to_index (layout_line,
+ x * PANGO_SCALE - x_offset,
+ &byte_index, &trailing);
+
+ gtk_text_btree_get_iter_at_line (layout->buffer->tree,
+ iter,
+ line, byte_index);
+
+ while (trailing--)
+ gtk_text_iter_forward_char (iter);
+
+ break;
+ }
+
+ byte_offset += layout_line->length;
+ tmp_list = tmp_list->next;
+ }
+
+ gtk_text_layout_free_line_display (layout, display);
+}
+
+/**
+ * gtk_text_layout_move_iter_visually:
+ * @layout: a #GtkTextLayout
+ * @iter: a #GtkTextIter
+ * @count: number of characters to move (negative moves left, positive moves right)
+ *
+ * Move the iterator a given number of characters visually, treating
+ * it as the strong cursor position. If @count is positive, then the
+ * new strong cursor position will be @count positions to the right of
+ * the old cursor position. If @count is negative then the new strong
+ * cursor position will be @count positions to the left of the old
+ * cursor position.
+ *
+ * In the presence of bidirection text, the correspondence
+ * between logical and visual order will depend on the direction
+ * of the current run, and there may be jumps when the cursor
+ * is moved off of the end of a run.
+ **/
+
+void
+gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
+ GtkTextIter *iter,
+ gint count)
+{
+ g_return_if_fail (layout != NULL);
+ g_return_if_fail (iter != NULL);
+
+ while (count != 0)
+ {
+ GtkTextLine *line = gtk_text_iter_get_line (iter);
+ gint line_byte = gtk_text_iter_get_line_byte (iter);
+ GtkTextLineDisplay *display = gtk_text_layout_get_line_display (layout, line, TRUE);
+ int byte_count = gtk_text_line_byte_count (line);
+
+ int new_index;
+ int new_trailing;
+
+ if (count > 0)
+ {
+ pango_layout_move_cursor_visually (display->layout, line_byte, 0, 1, &new_index, &new_trailing);
+ count--;
+ }
+ else
+ {
+ pango_layout_move_cursor_visually (display->layout, line_byte, 0, -1, &new_index, &new_trailing);
+ count++;
+ }
+
+ gtk_text_layout_free_line_display (layout, display);
+
+ if (new_index < 0)
+ {
+ line = gtk_text_line_previous (line);
+ if (!line)
+ return;
+
+ new_index = gtk_text_line_byte_count (line);
+
+ }
+ else if (new_index > byte_count)
+ {
+ line = gtk_text_line_next (line);
+ if (!line)
+ return;
+
+ new_index = 0;
+ }
+
+ gtk_text_btree_get_iter_at_line (layout->buffer->tree,
+ iter,
+ line, new_index);
+ while (new_trailing--)
+ gtk_text_iter_forward_char (iter);
+ }
+
+}
+
+typedef void (*GtkSignal_NONE__INT_INT_INT_INT) (GtkObject *object,
+ gint x, gint y,
+ gint width, gint height,
+ gpointer user_data);
+
+void
+gtk_marshal_NONE__INT_INT_INT_INT (GtkObject *object,
+ GtkSignalFunc func,
+ gpointer func_data,
+ GtkArg *args)
+{
+ GtkSignal_NONE__INT_INT_INT_INT rfunc;
+
+ rfunc = (GtkSignal_NONE__INT_INT_INT_INT) func;
+ (*rfunc) (object,
+ GTK_VALUE_INT (args[0]),
+ GTK_VALUE_INT (args[1]),
+ GTK_VALUE_INT (args[2]),
+ GTK_VALUE_INT (args[3]),
+ func_data);
+}
+
+void
+gtk_text_layout_spew (GtkTextLayout *layout)
+{
+#if 0
+ GtkTextDisplayLine *iter;
+ guint wrapped = 0;
+ guint paragraphs = 0;
+ GtkTextLine *last_line = NULL;
+
+ iter = layout->line_list;
+ while (iter != NULL)
+ {
+ if (iter->line != last_line)
+ {
+ printf ("%5u paragraph (%p)\n", paragraphs, iter->line);
+ ++paragraphs;
+ last_line = iter->line;
+ }
+
+ printf (" %5u y: %d len: %d start: %d bytes: %d\n",
+ wrapped, iter->y, iter->length, iter->byte_offset,
+ iter->byte_count);
+
+ ++wrapped;
+ iter = iter->next;
+ }
+
+ printf ("Layout %s recompute\n",
+ layout->need_recompute ? "needs" : "doesn't need");
+
+ printf ("Layout pars: %u lines: %u size: %d x %d Screen width: %d\n",
+ paragraphs, wrapped, layout->width,
+ layout->height, layout->screen_width);
+#endif
+}
+
+
diff --git a/gtk/gtktextlayout.h b/gtk/gtktextlayout.h
new file mode 100644
index 000000000..ffe270060
--- /dev/null
+++ b/gtk/gtktextlayout.h
@@ -0,0 +1,251 @@
+#ifndef GTK_TEXT_LAYOUT_H
+#define GTK_TEXT_LAYOUT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* This is a "semi-private" header; it is intended for
+ * use by the text widget, and the text canvas item,
+ * but that's all. We may have to install it so the
+ * canvas item can use it, but users are not supposed
+ * to use it.
+ */
+
+#include <gtk/gtktextbuffer.h>
+#include <gtk/gtktextiter.h>
+#include <gtk/gtktextbtree.h>
+
+
+#define GTK_TYPE_TEXT_LAYOUT (gtk_text_layout_get_type())
+#define GTK_TEXT_LAYOUT(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_TEXT_LAYOUT, GtkTextLayout))
+#define GTK_TEXT_LAYOUT_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TEXT_LAYOUT, GtkTextLayoutClass))
+#define GTK_IS_TEXT_LAYOUT(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_TEXT_LAYOUT))
+#define GTK_IS_TEXT_LAYOUT_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TEXT_LAYOUT))
+#define GTK_TEXT_LAYOUT_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_TEXT_LAYOUT, GtkTextLayoutClass))
+
+typedef struct _GtkTextLayoutClass GtkTextLayoutClass;
+typedef struct _GtkTextLineDisplay GtkTextLineDisplay;
+typedef struct _GtkTextCursorDisplay GtkTextCursorDisplay;
+typedef struct _GtkTextAttrAppearance GtkTextAttrAppearance;
+
+struct _GtkTextLayout
+{
+ GtkObject parent_instance;
+
+ /* width of the display area on-screen,
+ * i.e. pixels we should wrap to fit inside. */
+ gint screen_width;
+
+ /* width/height of the total logical area being layed out */
+ gint width;
+ gint height;
+
+ GtkTextBuffer *buffer;
+
+ /* Default style used if no tags override it */
+ GtkTextStyleValues *default_style;
+
+ /* Pango contexts used for creating layouts */
+ PangoContext *ltr_context;
+ PangoContext *rtl_context;
+
+ /* A cache of one style; this is used to ensure
+ * we don't constantly regenerate the style
+ * over long runs with the same style. */
+ GtkTextStyleValues *one_style_cache;
+
+ /* A cache of one line display. Getting the same line
+ * many times in a row is the most common case.
+ */
+ GtkTextLineDisplay *one_display_cache;
+
+ /* Whether we are allowed to wrap right now */
+ gint wrap_loop_count;
+};
+
+struct _GtkTextLayoutClass
+{
+ GtkObjectClass parent_class;
+
+ /* Some portion of the layout was invalidated
+ */
+ void (* invalidated) (GtkTextLayout *layout);
+
+ /* A range of the layout changed appearance and possibly height
+ */
+ void (* changed) (GtkTextLayout *layout,
+ gint y,
+ gint old_height,
+ gint new_height);
+
+
+ GtkTextLineData *(* wrap) (GtkTextLayout *layout,
+ GtkTextLine *line,
+ /* may be NULL */
+ GtkTextLineData *line_data);
+
+ void (* get_log_attrs) (GtkTextLayout *layout,
+ GtkTextLine *line,
+ PangoLogAttr **attrs,
+ gint *n_attrs);
+ void (* invalidate) (GtkTextLayout *layout,
+ const GtkTextIter *start,
+ const GtkTextIter *end);
+ void (* free_line_data) (GtkTextLayout *layout,
+ GtkTextLine *line,
+ GtkTextLineData *line_data);
+};
+
+struct _GtkTextAttrAppearance
+{
+ PangoAttribute attr;
+ GtkTextAppearance appearance;
+};
+
+struct _GtkTextCursorDisplay
+{
+ gint x;
+ gint y;
+ gint height;
+ guint is_strong : 1;
+ guint is_weak : 1;
+};
+
+struct _GtkTextLineDisplay
+{
+ PangoLayout *layout;
+ GSList *cursors;
+
+ GtkTextDirection direction;
+
+ gint width; /* Width of layout */
+ gint total_width; /* width - margins, if no width set on layout, if width set on layout, -1 */
+ gint height;
+ gint x_offset; /* Amount layout is shifted from left edge */
+ gint left_margin;
+ gint right_margin;
+ gint top_margin;
+ gint bottom_margin;
+
+ gboolean size_only;
+ GtkTextLine *line;
+};
+
+extern PangoAttrType gtk_text_attr_appearance_type;
+
+GtkType gtk_text_layout_get_type (void);
+GtkTextLayout *gtk_text_layout_new (void);
+
+void gtk_text_layout_set_buffer (GtkTextLayout *layout,
+ GtkTextBuffer *buffer);
+void gtk_text_layout_set_default_style (GtkTextLayout *layout,
+ GtkTextStyleValues *values);
+void gtk_text_layout_set_contexts (GtkTextLayout *layout,
+ PangoContext *ltr_context,
+ PangoContext *rtl_context);
+void gtk_text_layout_default_style_changed (GtkTextLayout *layout);
+void gtk_text_layout_set_screen_width (GtkTextLayout *layout,
+ gint width);
+
+/* Getting the size or the lines potentially results in a call to
+ * recompute, which is pretty massively expensive. Thus it should
+ * basically only be done in an idle handler.
+ *
+ * Long-term, we would really like to be able to do these without
+ * a full recompute so they may get cheaper over time.
+ */
+void gtk_text_layout_get_size (GtkTextLayout *layout,
+ gint *width,
+ gint *height);
+
+
+GSList *gtk_text_layout_get_lines (GtkTextLayout *layout,
+ /* [top_y, bottom_y) */
+ gint top_y,
+ gint bottom_y,
+ gint *first_line_y);
+
+void gtk_text_layout_wrap_loop_start (GtkTextLayout *layout);
+void gtk_text_layout_wrap_loop_end (GtkTextLayout *layout);
+
+GtkTextLineDisplay *gtk_text_layout_get_line_display (GtkTextLayout *layout,
+ GtkTextLine *line,
+ gboolean size_only);
+void gtk_text_layout_free_line_display (GtkTextLayout *layout,
+ GtkTextLineDisplay *display);
+
+void gtk_text_layout_get_line_at_y (GtkTextLayout *layout,
+ GtkTextIter *target_iter,
+ gint y,
+ gint *line_top);
+void gtk_text_layout_get_iter_at_pixel (GtkTextLayout *layout,
+ GtkTextIter *iter,
+ gint x,
+ gint y);
+void gtk_text_layout_invalidate (GtkTextLayout *layout,
+ const GtkTextIter *start,
+ const GtkTextIter *end);
+void gtk_text_layout_free_line_data (GtkTextLayout *layout,
+ GtkTextLine *line,
+ GtkTextLineData *line_data);
+
+gboolean gtk_text_layout_is_valid (GtkTextLayout *layout);
+void gtk_text_layout_validate_yrange (GtkTextLayout *layout,
+ GtkTextIter *anchor_line,
+ gint y0,
+ gint y1);
+void gtk_text_layout_validate (GtkTextLayout *layout,
+ gint max_pixels);
+
+ /* This function should return the passed-in line data,
+ OR remove the existing line data from the line, and
+ return a NEW line data after adding it to the line.
+ That is, invariant after calling the callback is that
+ there should be exactly one line data for this view
+ stored on the btree line. */
+GtkTextLineData *gtk_text_layout_wrap (GtkTextLayout *layout,
+ GtkTextLine *line,
+ /* may be NULL */
+ GtkTextLineData *line_data);
+void gtk_text_layout_get_log_attrs (GtkTextLayout *layout,
+ GtkTextLine *line,
+ PangoLogAttr **attrs,
+ gint *n_attrs);
+void gtk_text_layout_changed (GtkTextLayout *layout,
+ gint y,
+ gint old_height,
+ gint new_height);
+void gtk_text_layout_get_iter_location (GtkTextLayout *layout,
+ const GtkTextIter *iter,
+ GdkRectangle *rect);
+gint gtk_text_layout_get_line_y (GtkTextLayout *layout,
+ const GtkTextIter *iter);
+void gtk_text_layout_get_cursor_locations (GtkTextLayout *layout,
+ GtkTextIter *iter,
+ GdkRectangle *strong_pos,
+ GdkRectangle *weak_pos);
+gboolean gtk_text_layout_clamp_iter_to_vrange (GtkTextLayout *layout,
+ GtkTextIter *iter,
+ gint top,
+ gint bottom);
+
+void gtk_text_layout_move_iter_to_previous_line (GtkTextLayout *layout,
+ GtkTextIter *iter);
+void gtk_text_layout_move_iter_to_next_line (GtkTextLayout *layout,
+ GtkTextIter *iter);
+void gtk_text_layout_move_iter_to_x (GtkTextLayout *layout,
+ GtkTextIter *iter,
+ gint x);
+void gtk_text_layout_move_iter_visually (GtkTextLayout *layout,
+ GtkTextIter *iter,
+ gint count);
+
+
+void gtk_text_layout_spew (GtkTextLayout *layout);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
diff --git a/gtk/gtktextmark.c b/gtk/gtktextmark.c
new file mode 100644
index 000000000..739504732
--- /dev/null
+++ b/gtk/gtktextmark.c
@@ -0,0 +1,339 @@
+/*
+ * tkTextMark.c --
+ *
+ * This file contains the procedure that implement marks for
+ * text widgets.
+ *
+ * Copyright (c) 1994 The Regents of the University of California.
+ * Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ *
+ * See the file "license.terms" for information on usage and redistribution
+ * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ *
+ * RCS: @(#) $Id$
+ */
+
+#include "gtktextbtree.h"
+
+gboolean
+gtk_text_mark_is_visible(GtkTextMark *mark)
+{
+ GtkTextLineSegment *seg;
+
+ seg = (GtkTextLineSegment*)mark;
+
+ return seg->body.mark.visible;
+}
+
+char *
+gtk_text_mark_get_name (GtkTextMark *mark)
+{
+ GtkTextLineSegment *seg;
+
+ seg = (GtkTextLineSegment*)mark;
+
+ return g_strdup (seg->body.mark.name);
+}
+
+/*
+ * Macro that determines the size of a mark segment:
+ */
+
+#define MSEG_SIZE ((unsigned) (G_STRUCT_OFFSET(GtkTextLineSegment, body) \
+ + sizeof(GtkTextMarkBody)))
+
+
+GtkTextLineSegment*
+mark_segment_new(GtkTextBTree *tree,
+ gboolean left_gravity,
+ const gchar *name)
+{
+ GtkTextLineSegment *mark;
+
+ mark = (GtkTextLineSegment *) g_malloc0(MSEG_SIZE);
+ mark->body.mark.name = g_strdup(name);
+
+ if (left_gravity)
+ mark->type = &gtk_text_left_mark_type;
+ else
+ mark->type = &gtk_text_right_mark_type;
+
+ mark->byte_count = 0;
+ mark->char_count = 0;
+
+ mark->body.mark.tree = tree;
+ mark->body.mark.line = NULL;
+ mark->next = NULL;
+
+ mark->body.mark.refcount = 1;
+
+ mark->body.mark.visible = FALSE;
+
+ return mark;
+}
+
+void
+mark_segment_ref(GtkTextLineSegment *mark)
+{
+ g_return_if_fail(mark != NULL);
+ g_return_if_fail(mark->type == &gtk_text_right_mark_type ||
+ mark->type == &gtk_text_left_mark_type);
+ g_return_if_fail(mark->body.mark.refcount > 0);
+
+ mark->body.mark.refcount += 1;
+}
+
+void
+mark_segment_unref(GtkTextLineSegment *mark)
+{
+ g_return_if_fail(mark != NULL);
+ g_return_if_fail(mark->type == &gtk_text_right_mark_type ||
+ mark->type == &gtk_text_left_mark_type);
+ g_return_if_fail(mark->body.mark.refcount > 0);
+
+ mark->body.mark.refcount -= 1;
+
+ if (mark->body.mark.refcount == 0)
+ {
+ g_free(mark->body.mark.name);
+
+ g_free(mark);
+ }
+}
+
+/*
+ * Forward references for procedures defined in this file:
+ */
+
+static int mark_segment_delete_func (GtkTextLineSegment *segPtr,
+ GtkTextLine *line,
+ int treeGone);
+static GtkTextLineSegment *mark_segment_cleanup_func (GtkTextLineSegment *segPtr,
+ GtkTextLine *line);
+static void mark_segment_check_func (GtkTextLineSegment *segPtr,
+ GtkTextLine *line);
+
+/*
+ * The following structures declare the "mark" segment types.
+ * There are actually two types for marks, one with left gravity
+ * and one with right gravity. They are identical except for
+ * their gravity property.
+ */
+
+GtkTextLineSegmentClass gtk_text_right_mark_type = {
+ "mark", /* name */
+ FALSE, /* leftGravity */
+ (GtkTextLineSegmentSplitFunc) NULL, /* splitFunc */
+ mark_segment_delete_func, /* deleteFunc */
+ mark_segment_cleanup_func, /* cleanupFunc */
+ (GtkTextLineSegmentLineChangeFunc) NULL, /* lineChangeFunc */
+ mark_segment_check_func /* checkFunc */
+};
+
+GtkTextLineSegmentClass gtk_text_left_mark_type = {
+ "mark", /* name */
+ TRUE, /* leftGravity */
+ (GtkTextLineSegmentSplitFunc) NULL, /* splitFunc */
+ mark_segment_delete_func, /* deleteFunc */
+ mark_segment_cleanup_func, /* cleanupFunc */
+ (GtkTextLineSegmentLineChangeFunc) NULL, /* lineChangeFunc */
+ mark_segment_check_func /* checkFunc */
+};
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * mark_segment_delete_func --
+ *
+ * This procedure is invoked by the text B-tree code whenever
+ * a mark lies in a range of characters being deleted.
+ *
+ * Results:
+ * Returns 1 to indicate that deletion has been rejected.
+ *
+ * Side effects:
+ * None (even if the whole tree is being deleted we don't
+ * free up the mark; it will be done elsewhere).
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static int
+mark_segment_delete_func(segPtr, line, treeGone)
+ GtkTextLineSegment *segPtr; /* Segment being deleted. */
+ GtkTextLine *line; /* Line containing segment. */
+ int treeGone; /* Non-zero means the entire tree is
+ * being deleted, so everything must
+ * get cleaned up. */
+{
+ return 1;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * mark_segment_cleanup_func --
+ *
+ * This procedure is invoked by the B-tree code whenever a
+ * mark segment is moved from one line to another.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The line field of the segment gets updated.
+ *
+ *--------------------------------------------------------------
+ */
+
+static GtkTextLineSegment *
+mark_segment_cleanup_func(markPtr, line)
+ GtkTextLineSegment *markPtr; /* Mark segment that's being moved. */
+ GtkTextLine *line; /* Line that now contains segment. */
+{
+ markPtr->body.mark.line = line;
+ return markPtr;
+}
+
+#if 0
+
+
+/*
+ *--------------------------------------------------------------
+ *
+ * GtkTextInsertDisplayFunc --
+ *
+ * This procedure is called to display the insertion
+ * cursor.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Graphics are drawn.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+void
+GtkTextInsertDisplayFunc(chunkPtr, x, y, height, baseline, display, dst, screenY)
+ GtkTextDisplayChunk *chunkPtr; /* Chunk that is to be drawn. */
+ int x; /* X-position in dst at which to
+ * draw this chunk (may differ from
+ * the x-position in the chunk because
+ * of scrolling). */
+ int y; /* Y-position at which to draw this
+ * chunk in dst (x-position is in
+ * the chunk itself). */
+ int height; /* Total height of line. */
+ int baseline; /* Offset of baseline from y. */
+ Display *display; /* Display to use for drawing. */
+ Drawable dst; /* Pixmap or window in which to draw
+ * chunk. */
+ int screenY; /* Y-coordinate in text window that
+ * corresponds to y. */
+{
+ GtkTextView *tkxt = (GtkTextView *) chunkPtr->clientData;
+ int halfWidth = tkxt->insertWidth/2;
+
+ if ((x + halfWidth) < 0) {
+ /*
+ * The insertion cursor is off-screen. Just return.
+ */
+
+ return;
+ }
+
+ /*
+ * As a special hack to keep the cursor visible on mono displays
+ * (or anywhere else that the selection and insertion cursors
+ * have the same color) write the default background in the cursor
+ * area (instead of nothing) when the cursor isn't on. Otherwise
+ * the selection might hide the cursor.
+ */
+
+ if (tkxt->flags & INSERT_ON) {
+ Tk_Fill3DRectangle(tkxt->tkwin, dst, tkxt->insertBorder,
+ x - tkxt->insertWidth/2, y, tkxt->insertWidth,
+ height, tkxt->insertBorderWidth, TK_RELIEF_RAISED);
+ } else if (tkxt->selBorder == tkxt->insertBorder) {
+ Tk_Fill3DRectangle(tkxt->tkwin, dst, tkxt->border,
+ x - tkxt->insertWidth/2, y, tkxt->insertWidth,
+ height, 0, TK_RELIEF_FLAT);
+ }
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * InsertUndisplayFunc --
+ *
+ * This procedure is called when the insertion cursor is no
+ * longer at a visible point on the display. It does nothing
+ * right now.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * None.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static void
+InsertUndisplayFunc(tkxt, chunkPtr)
+ GtkTextView *tkxt; /* Overall information about text
+ * widget. */
+ GtkTextDisplayChunk *chunkPtr; /* Chunk that is about to be freed. */
+{
+ return;
+}
+
+#endif
+/*
+ *--------------------------------------------------------------
+ *
+ * mark_segment_check_func --
+ *
+ * This procedure is invoked by the B-tree code to perform
+ * consistency checks on mark segments.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * The procedure panics if it detects anything wrong with
+ * the mark.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+mark_segment_check_func(markPtr, line)
+ GtkTextLineSegment *markPtr; /* Segment to check. */
+ GtkTextLine *line; /* Line containing segment. */
+{
+ if (markPtr->body.mark.line != line)
+ g_error("mark_segment_check_func: markPtr->body.mark.line bogus");
+
+ /* No longer do this because we don't have access to btree
+ struct members */
+#if 0
+ /*
+ * Make sure that the mark is still present in the text's mark
+ * hash table.
+ */
+ for (hPtr = Tcl_FirstHashEntry(&markPtr->body.mark.tkxt->markTable,
+ &search); hPtr != markPtr->body.mark.hPtr;
+ hPtr = Tcl_NextHashEntry(&search)) {
+ if (hPtr == NULL) {
+ panic("mark_segment_check_func couldn't find hash table entry for mark");
+ }
+ }
+#endif
+}
diff --git a/gtk/gtktextmark.h b/gtk/gtktextmark.h
new file mode 100644
index 000000000..0fa2f0dd5
--- /dev/null
+++ b/gtk/gtktextmark.h
@@ -0,0 +1,24 @@
+#ifndef GTK_TEXT_MARK_H
+#define GTK_TEXT_MARK_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/* The GtkTextMark data type */
+
+typedef struct _GtkTextMark GtkTextMark;
+
+void gtk_text_mark_set_visible (GtkTextMark *mark,
+ gboolean setting);
+
+gboolean gtk_text_mark_is_visible (GtkTextMark *mark);
+char * gtk_text_mark_get_name (GtkTextMark *mark);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
+
diff --git a/gtk/gtktextmarkprivate.h b/gtk/gtktextmarkprivate.h
new file mode 100644
index 000000000..20ed1d5b0
--- /dev/null
+++ b/gtk/gtktextmarkprivate.h
@@ -0,0 +1,37 @@
+#ifndef GTK_TEXT_MARK_PRIVATE_H
+#define GTK_TEXT_MARK_PRIVATE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <gtk/gtktexttypes.h>
+
+/*
+ * The data structure below defines line segments that represent
+ * marks. There is one of these for each mark in the text.
+ */
+
+struct _GtkTextMarkBody {
+ guint refcount;
+ gchar *name;
+ GtkTextBTree *tree;
+ GtkTextLine *line;
+ gboolean visible;
+};
+
+GtkTextLineSegment *mark_segment_new (GtkTextBTree *tree,
+ gboolean left_gravity,
+ const gchar *name);
+void mark_segment_ref (GtkTextLineSegment *mark);
+void mark_segment_unref (GtkTextLineSegment *mark);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
+
+
diff --git a/gtk/gtktextsegment.c b/gtk/gtktextsegment.c
new file mode 100644
index 000000000..e4effa1b5
--- /dev/null
+++ b/gtk/gtktextsegment.c
@@ -0,0 +1,626 @@
+/*
+ * gtktextsegment.c --
+ *
+ * Code for segments in general, and toggle/char segments in particular.
+ *
+ * Copyright (c) 1992-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1995 Sun Microsystems, Inc.
+ * Copyright (c) 2000 Red Hat, Inc.
+ * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
+ *
+ * This software is copyrighted by the Regents of the University of
+ * California, Sun Microsystems, Inc., and other parties. The
+ * following terms apply to all files associated with the software
+ * unless explicitly disclaimed in individual files.
+ *
+ * The authors hereby grant permission to use, copy, modify,
+ * distribute, and license this software and its documentation for any
+ * purpose, provided that existing copyright notices are retained in
+ * all copies and that this notice is included verbatim in any
+ * distributions. No written agreement, license, or royalty fee is
+ * required for any of the authorized uses. Modifications to this
+ * software may be copyrighted by their authors and need not follow
+ * the licensing terms described here, provided that the new terms are
+ * clearly indicated on the first page of each file where they apply.
+ *
+ * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+ * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
+ * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
+ * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
+ * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
+ * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * GOVERNMENT USE: If you are acquiring this software on behalf of the
+ * U.S. government, the Government shall have only "Restricted Rights"
+ * in the software and related documentation as defined in the Federal
+ * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
+ * are acquiring the software on behalf of the Department of Defense,
+ * the software shall be classified as "Commercial Computer Software"
+ * and the Government shall have only "Restricted Rights" as defined
+ * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
+ * foregoing, the authors grant the U.S. Government and others acting
+ * in its behalf permission to use and distribute the software in
+ * accordance with the terms specified in this license.
+ *
+ */
+
+#include "gtktextbtree.h"
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "gtktexttag.h"
+#include "gtktexttagtable.h"
+#include "gtktextlayout.h"
+#include "gtktextiterprivate.h"
+#include "gtkdebug.h"
+
+/*
+ *--------------------------------------------------------------
+ *
+ * split_segment --
+ *
+ * This procedure is called before adding or deleting
+ * segments. It does three things: (a) it finds the segment
+ * containing iter; (b) if there are several such
+ * segments (because some segments have zero length) then
+ * it picks the first segment that does not have left
+ * gravity; (c) if the index refers to the middle of
+ * a segment then it splits the segment so that the
+ * index now refers to the beginning of a segment.
+ *
+ * Results:
+ * The return value is a pointer to the segment just
+ * before the segment corresponding to iter (as
+ * described above). If the segment corresponding to
+ * iter is the first in its line then the return
+ * value is NULL.
+ *
+ * Side effects:
+ * The segment referred to by iter is split unless
+ * iter refers to its first character.
+ *
+ *--------------------------------------------------------------
+ */
+
+GtkTextLineSegment*
+gtk_text_line_segment_split(const GtkTextIter *iter)
+{
+ GtkTextLineSegment *prev, *seg;
+ GtkTextBTree *tree;
+ GtkTextLine *line;
+ int count;
+
+ line = gtk_text_iter_get_line(iter);
+ tree = gtk_text_iter_get_btree(iter);
+
+ count = gtk_text_iter_get_line_byte(iter);
+
+ prev = NULL;
+ seg = line->segments;
+
+ while (seg != NULL)
+ {
+ if (seg->byte_count > count)
+ {
+ if (count == 0)
+ {
+ return prev;
+ }
+ else
+ {
+ g_assert(count != seg->byte_count);
+ g_assert(seg->byte_count > 0);
+
+ gtk_text_btree_segments_changed(tree);
+
+ seg = (*seg->type->splitFunc)(seg, count);
+
+ if (prev == NULL)
+ line->segments = seg;
+ else
+ prev->next = seg;
+
+ return seg;
+ }
+ }
+ else if ((seg->byte_count == 0) && (count == 0)
+ && !seg->type->leftGravity)
+ {
+ return prev;
+ }
+
+ count -= seg->byte_count;
+ prev = seg;
+ seg = seg->next;
+ }
+ g_error("split_segment reached end of line!");
+ return NULL;
+}
+
+
+/*
+ * Macros that determine how much space to allocate for new segments:
+ */
+
+#define CSEG_SIZE(chars) ((unsigned) (G_STRUCT_OFFSET(GtkTextLineSegment, body) \
+ + 1 + (chars)))
+#define TSEG_SIZE ((unsigned) (G_STRUCT_OFFSET(GtkTextLineSegment, body) \
+ + sizeof(GtkTextToggleBody)))
+
+/*
+ * Type functions
+ */
+
+static void
+char_segment_self_check(GtkTextLineSegment *seg)
+{
+ /* This function checks the segment itself, but doesn't
+ assume the segment has been validly inserted into
+ the btree. */
+
+ g_assert(seg != NULL);
+
+ if (seg->byte_count <= 0)
+ {
+ g_error("char_segment_check_func: segment has size <= 0");
+ }
+
+ if (strlen(seg->body.chars) != seg->byte_count)
+ {
+ g_error("char_segment_check_func: segment has wrong size");
+ }
+
+ if (gtk_text_view_num_utf_chars(seg->body.chars, seg->byte_count) != seg->char_count)
+ {
+ g_error("char segment has wrong character count");
+ }
+}
+
+GtkTextLineSegment*
+char_segment_new(const gchar *text, guint len)
+{
+ GtkTextLineSegment *seg;
+
+ g_assert(gtk_text_byte_begins_utf8_char(text));
+
+ seg = g_malloc(CSEG_SIZE(len));
+ seg->type = &gtk_text_char_type;
+ seg->next = NULL;
+ seg->byte_count = len;
+ memcpy(seg->body.chars, text, len);
+ seg->body.chars[len] = '\0';
+
+ seg->char_count = gtk_text_view_num_utf_chars(seg->body.chars, seg->byte_count);
+
+ if (gtk_debug_flags & GTK_DEBUG_TEXT)
+ char_segment_self_check(seg);
+
+ return seg;
+}
+
+GtkTextLineSegment*
+char_segment_new_from_two_strings(const gchar *text1, guint len1,
+ const gchar *text2, guint len2)
+{
+ GtkTextLineSegment *seg;
+
+ g_assert(gtk_text_byte_begins_utf8_char(text1));
+ g_assert(gtk_text_byte_begins_utf8_char(text2));
+
+ seg = g_malloc(CSEG_SIZE(len1+len2));
+ seg->type = &gtk_text_char_type;
+ seg->next = NULL;
+ seg->byte_count = len1 + len2;
+ memcpy(seg->body.chars, text1, len1);
+ memcpy(seg->body.chars + len1, text2, len2);
+ seg->body.chars[len1+len2] = '\0';
+
+ /* In principle this function could probably take chars1 and chars2
+ as args, since it's typically used to merge two char segments */
+ seg->char_count = gtk_text_view_num_utf_chars(seg->body.chars, seg->byte_count);
+
+ if (gtk_debug_flags & GTK_DEBUG_TEXT)
+ char_segment_self_check(seg);
+
+ return seg;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * char_segment_split_func --
+ *
+ * This procedure implements splitting for character segments.
+ *
+ * Results:
+ * The return value is a pointer to a chain of two segments
+ * that have the same characters as segPtr except split
+ * among the two segments.
+ *
+ * Side effects:
+ * Storage for segPtr is freed.
+ *
+ *--------------------------------------------------------------
+ */
+
+static GtkTextLineSegment *
+char_segment_split_func(GtkTextLineSegment *seg, int index)
+{
+ GtkTextLineSegment *new1, *new2;
+
+ g_assert(index < seg->byte_count);
+
+ if (gtk_debug_flags & GTK_DEBUG_TEXT)
+ {
+ char_segment_self_check(seg);
+ }
+
+ new1 = char_segment_new(seg->body.chars, index);
+ new2 = char_segment_new(seg->body.chars + index, seg->byte_count - index);
+
+ g_assert(gtk_text_byte_begins_utf8_char(new1->body.chars));
+ g_assert(gtk_text_byte_begins_utf8_char(new2->body.chars));
+ g_assert(new1->byte_count + new2->byte_count == seg->byte_count);
+ g_assert(new1->char_count + new2->char_count == seg->char_count);
+
+ new1->next = new2;
+ new2->next = seg->next;
+
+ if (gtk_debug_flags & GTK_DEBUG_TEXT)
+ {
+ char_segment_self_check(new1);
+ char_segment_self_check(new2);
+ }
+
+ g_free(seg);
+ return new1;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * char_segment_cleanup_func --
+ *
+ * This procedure merges adjacent character segments into
+ * a single character segment, if possible.
+ *
+ * Results:
+ * The return value is a pointer to the first segment in
+ * the (new) list of segments that used to start with segPtr.
+ *
+ * Side effects:
+ * Storage for the segments may be allocated and freed.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static GtkTextLineSegment *
+char_segment_cleanup_func(segPtr, line)
+ GtkTextLineSegment *segPtr; /* Pointer to first of two adjacent
+ * segments to join. */
+ GtkTextLine *line; /* Line containing segments (not
+ * used). */
+{
+ GtkTextLineSegment *segPtr2, *newPtr;
+
+ if (gtk_debug_flags & GTK_DEBUG_TEXT)
+ char_segment_self_check(segPtr);
+
+ segPtr2 = segPtr->next;
+ if ((segPtr2 == NULL) || (segPtr2->type != &gtk_text_char_type))
+ {
+ return segPtr;
+ }
+
+ newPtr = char_segment_new_from_two_strings(segPtr->body.chars, segPtr->byte_count,
+ segPtr2->body.chars, segPtr2->byte_count);
+
+ newPtr->next = segPtr2->next;
+
+ if (gtk_debug_flags & GTK_DEBUG_TEXT)
+ char_segment_self_check(newPtr);
+
+ g_free(segPtr);
+ g_free(segPtr2);
+ return newPtr;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * char_segment_delete_func --
+ *
+ * This procedure is invoked to delete a character segment.
+ *
+ * Results:
+ * Always returns 0 to indicate that the segment was deleted.
+ *
+ * Side effects:
+ * Storage for the segment is freed.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static int
+char_segment_delete_func(segPtr, line, treeGone)
+ GtkTextLineSegment *segPtr; /* Segment to delete. */
+ GtkTextLine *line; /* Line containing segment. */
+ int treeGone; /* Non-zero means the entire tree is
+ * being deleted, so everything must
+ * get cleaned up. */
+{
+ g_free((char*) segPtr);
+ return 0;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * char_segment_check_func --
+ *
+ * This procedure is invoked to perform consistency checks
+ * on character segments.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * If the segment isn't inconsistent then the procedure
+ * g_errors.
+ *
+ *--------------------------------------------------------------
+ */
+
+ /* ARGSUSED */
+static void
+char_segment_check_func(segPtr, line)
+ GtkTextLineSegment *segPtr; /* Segment to check. */
+ GtkTextLine *line; /* Line containing segment. */
+{
+ char_segment_self_check(segPtr);
+
+ if (segPtr->next == NULL)
+ {
+ if (segPtr->body.chars[segPtr->byte_count-1] != '\n')
+ {
+ g_error("char_segment_check_func: line doesn't end with newline");
+ }
+ }
+ else
+ {
+ if (segPtr->next->type == &gtk_text_char_type)
+ {
+ g_error("char_segment_check_func: adjacent character segments weren't merged");
+ }
+ }
+}
+
+GtkTextLineSegment*
+toggle_segment_new(GtkTextTagInfo *info, gboolean on)
+{
+ GtkTextLineSegment *seg;
+
+ seg = g_malloc(TSEG_SIZE);
+
+ seg->type = on ? &gtk_text_toggle_on_type : &gtk_text_toggle_off_type;
+
+ seg->next = NULL;
+
+ seg->byte_count = 0;
+ seg->char_count = 0;
+
+ seg->body.toggle.info = info;
+ seg->body.toggle.inNodeCounts = 0;
+
+ return seg;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * toggle_segment_delete_func --
+ *
+ * This procedure is invoked to delete toggle segments.
+ *
+ * Results:
+ * Returns 1 to indicate that the segment may not be deleted,
+ * unless the entire B-tree is going away.
+ *
+ * Side effects:
+ * If the tree is going away then the toggle's memory is
+ * freed; otherwise the toggle counts in GtkTextBTreeNodes above the
+ * segment get updated.
+ *
+ *--------------------------------------------------------------
+ */
+
+static int
+toggle_segment_delete_func(segPtr, line, treeGone)
+ GtkTextLineSegment *segPtr; /* Segment to check. */
+ GtkTextLine *line; /* Line containing segment. */
+ int treeGone; /* Non-zero means the entire tree is
+ * being deleted, so everything must
+ * get cleaned up. */
+{
+ if (treeGone)
+ {
+ g_free((char *) segPtr);
+ return 0;
+ }
+
+ /*
+ * This toggle is in the middle of a range of characters that's
+ * being deleted. Refuse to die. We'll be moved to the end of
+ * the deleted range and our cleanup procedure will be called
+ * later. Decrement GtkTextBTreeNode toggle counts here, and set a flag
+ * so we'll re-increment them in the cleanup procedure.
+ */
+
+ if (segPtr->body.toggle.inNodeCounts)
+ {
+ change_node_toggle_count(line->parent,
+ segPtr->body.toggle.info, -1);
+ segPtr->body.toggle.inNodeCounts = 0;
+ }
+ return 1;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * toggle_segment_cleanup_func --
+ *
+ * This procedure is called when a toggle is part of a line that's
+ * been modified in some way. It's invoked after the
+ * modifications are complete.
+ *
+ * Results:
+ * The return value is the head segment in a new list
+ * that is to replace the tail of the line that used to
+ * start at segPtr. This allows the procedure to delete
+ * or modify segPtr.
+ *
+ * Side effects:
+ * Toggle counts in the GtkTextBTreeNodes above the new line will be
+ * updated if they're not already. Toggles may be collapsed
+ * if there are duplicate toggles at the same position.
+ *
+ *--------------------------------------------------------------
+ */
+
+static GtkTextLineSegment *
+toggle_segment_cleanup_func(segPtr, line)
+ GtkTextLineSegment *segPtr; /* Segment to check. */
+ GtkTextLine *line; /* Line that now contains segment. */
+{
+ GtkTextLineSegment *segPtr2, *prevPtr;
+ int counts;
+
+ /*
+ * If this is a toggle-off segment, look ahead through the next
+ * segments to see if there's a toggle-on segment for the same tag
+ * before any segments with non-zero size. If so then the two
+ * toggles cancel each other; remove them both.
+ */
+
+ if (segPtr->type == &gtk_text_toggle_off_type)
+ {
+ for (prevPtr = segPtr, segPtr2 = prevPtr->next;
+ (segPtr2 != NULL) && (segPtr2->byte_count == 0);
+ prevPtr = segPtr2, segPtr2 = prevPtr->next)
+ {
+ if (segPtr2->type != &gtk_text_toggle_on_type)
+ {
+ continue;
+ }
+ if (segPtr2->body.toggle.info != segPtr->body.toggle.info)
+ {
+ continue;
+ }
+ counts = segPtr->body.toggle.inNodeCounts
+ + segPtr2->body.toggle.inNodeCounts;
+ if (counts != 0)
+ {
+ change_node_toggle_count(line->parent,
+ segPtr->body.toggle.info, -counts);
+ }
+ prevPtr->next = segPtr2->next;
+ g_free((char *) segPtr2);
+ segPtr2 = segPtr->next;
+ g_free((char *) segPtr);
+ return segPtr2;
+ }
+ }
+
+ if (!segPtr->body.toggle.inNodeCounts)
+ {
+ change_node_toggle_count(line->parent,
+ segPtr->body.toggle.info, 1);
+ segPtr->body.toggle.inNodeCounts = 1;
+ }
+ return segPtr;
+}
+
+/*
+ *--------------------------------------------------------------
+ *
+ * toggle_segment_line_change_func --
+ *
+ * This procedure is invoked when a toggle segment is about
+ * to move from one line to another.
+ *
+ * Results:
+ * None.
+ *
+ * Side effects:
+ * Toggle counts are decremented in the GtkTextBTreeNodes above the line.
+ *
+ *--------------------------------------------------------------
+ */
+
+static void
+toggle_segment_line_change_func(segPtr, line)
+ GtkTextLineSegment *segPtr; /* Segment to check. */
+ GtkTextLine *line; /* Line that used to contain segment. */
+{
+ if (segPtr->body.toggle.inNodeCounts)
+ {
+ change_node_toggle_count(line->parent,
+ segPtr->body.toggle.info, -1);
+ segPtr->body.toggle.inNodeCounts = 0;
+ }
+}
+
+/*
+ * Virtual tables
+ */
+
+
+GtkTextLineSegmentClass gtk_text_char_type = {
+ "character", /* name */
+ 0, /* leftGravity */
+ char_segment_split_func, /* splitFunc */
+ char_segment_delete_func, /* deleteFunc */
+ char_segment_cleanup_func, /* cleanupFunc */
+ NULL, /* lineChangeFunc */
+ char_segment_check_func /* checkFunc */
+};
+
+/*
+ * Type record for segments marking the beginning of a tagged
+ * range:
+ */
+
+GtkTextLineSegmentClass gtk_text_toggle_on_type = {
+ "toggleOn", /* name */
+ 0, /* leftGravity */
+ NULL, /* splitFunc */
+ toggle_segment_delete_func, /* deleteFunc */
+ toggle_segment_cleanup_func, /* cleanupFunc */
+ toggle_segment_line_change_func, /* lineChangeFunc */
+ toggle_segment_check_func /* checkFunc */
+};
+
+/*
+ * Type record for segments marking the end of a tagged
+ * range:
+ */
+
+GtkTextLineSegmentClass gtk_text_toggle_off_type = {
+ "toggleOff", /* name */
+ 1, /* leftGravity */
+ NULL, /* splitFunc */
+ toggle_segment_delete_func, /* deleteFunc */
+ toggle_segment_cleanup_func, /* cleanupFunc */
+ toggle_segment_line_change_func, /* lineChangeFunc */
+ toggle_segment_check_func /* checkFunc */
+};
diff --git a/gtk/gtktextsegment.h b/gtk/gtktextsegment.h
new file mode 100644
index 000000000..d3fe77e82
--- /dev/null
+++ b/gtk/gtktextsegment.h
@@ -0,0 +1,128 @@
+#ifndef GTK_TEXT_SEGMENT_H
+#define GTK_TEXT_SEGMENT_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/*
+ * Segments: each line is divided into one or more segments, where each
+ * segment is one of several things, such as a group of characters, a
+ * tag toggle, a mark, or an embedded widget. Each segment starts with
+ * a standard header followed by a body that varies from type to type.
+ */
+
+/* This header has the segment type, and two specific segments
+ (character and toggle segments) */
+
+#include <gtk/gtktexttag.h>
+#include <gtk/gtktextiter.h>
+#include <gtk/gtktextmarkprivate.h>
+#include <gtk/gtktextchild.h>
+
+/* Information a BTree stores about a tag. */
+typedef struct _GtkTextTagInfo GtkTextTagInfo;
+struct _GtkTextTagInfo {
+ GtkTextTag *tag;
+ GtkTextBTreeNode *tag_root; /* highest-level node containing the tag */
+ gint toggle_count; /* total toggles of this tag below tag_root */
+};
+
+/* Body of a segment that toggles a tag on or off */
+struct _GtkTextToggleBody {
+ GtkTextTagInfo *info; /* Tag that starts or ends here. */
+ gboolean inNodeCounts; /* TRUE means this toggle has been
+ * accounted for in node toggle
+ * counts; FALSE means it hasn't, yet. */
+};
+
+
+/* Class struct for segments */
+
+typedef GtkTextLineSegment *(* GtkTextLineSegmentSplitFunc) (GtkTextLineSegment *segPtr,
+ int index);
+typedef gboolean (* GtkTextViewSegDeleteFunc) (GtkTextLineSegment *segPtr,
+ GtkTextLine *line,
+ gboolean treeGone);
+typedef GtkTextLineSegment *(* GtkTextViewSegCleanupFunc) (GtkTextLineSegment *segPtr,
+ GtkTextLine *line);
+typedef void (* GtkTextLineSegmentLineChangeFunc) (GtkTextLineSegment *segPtr,
+ GtkTextLine *line);
+typedef void (* GtkTextViewSegCheckFunc) (GtkTextLineSegment *segPtr,
+ GtkTextLine *line);
+
+struct _GtkTextLineSegmentClass {
+ char *name; /* Name of this kind of segment. */
+ gboolean leftGravity; /* If a segment has zero size (e.g. a
+ * mark or tag toggle), does it
+ * attach to character to its left
+ * or right? 1 means left, 0 means
+ * right. */
+ GtkTextLineSegmentSplitFunc splitFunc; /* Procedure to split large segment
+ * into two smaller ones. */
+ GtkTextViewSegDeleteFunc deleteFunc; /* Procedure to call to delete
+ * segment. */
+ GtkTextViewSegCleanupFunc cleanupFunc; /* After any change to a line, this
+ * procedure is invoked for all
+ * segments left in the line to
+ * perform any cleanup they wish
+ * (e.g. joining neighboring
+ * segments). */
+ GtkTextLineSegmentLineChangeFunc lineChangeFunc;
+ /* Invoked when a segment is about
+ * to be moved from its current line
+ * to an earlier line because of
+ * a deletion. The line is that
+ * for the segment's old line.
+ * CleanupFunc will be invoked after
+ * the deletion is finished. */
+
+ GtkTextViewSegCheckFunc checkFunc; /* Called during consistency checks
+ * to check internal consistency of
+ * segment. */
+};
+
+/*
+ * The data structure below defines line segments.
+ */
+
+struct _GtkTextLineSegment {
+ GtkTextLineSegmentClass *type; /* Pointer to record describing
+ * segment's type. */
+ GtkTextLineSegment *next; /* Next in list of segments for this
+ * line, or NULL for end of list. */
+
+ int char_count; /* # of chars of index space occupied */
+
+ int byte_count; /* Size of this segment (# of bytes
+ * of index space it occupies). */
+ union {
+ char chars[4]; /* Characters that make up character
+ * info. Actual length varies to
+ * hold as many characters as needed.*/
+ GtkTextToggleBody toggle; /* Information about tag toggle. */
+ GtkTextMarkBody mark; /* Information about mark. */
+ GtkTextPixmap pixmap; /* Child pixmap */
+#if 0
+ GtkTextChild child; /* child widget */
+#endif
+ } body;
+};
+
+
+GtkTextLineSegment *gtk_text_line_segment_split(const GtkTextIter *iter);
+
+GtkTextLineSegment *char_segment_new(const gchar *text, guint len);
+
+GtkTextLineSegment *char_segment_new_from_two_strings(const gchar *text1, guint len1,
+ const gchar *text2, guint len2);
+
+GtkTextLineSegment *toggle_segment_new(GtkTextTagInfo *info, gboolean on);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
+
diff --git a/gtk/gtktexttag.c b/gtk/gtktexttag.c
new file mode 100644
index 000000000..0caf021be
--- /dev/null
+++ b/gtk/gtktexttag.c
@@ -0,0 +1,1272 @@
+/* gtktexttag.c - text tag object
+ *
+ * Copyright (c) 1992-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1997 Sun Microsystems, Inc.
+ * Copyright (c) 2000 Red Hat, Inc.
+ * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
+ *
+ * This software is copyrighted by the Regents of the University of
+ * California, Sun Microsystems, Inc., and other parties. The
+ * following terms apply to all files associated with the software
+ * unless explicitly disclaimed in individual files.
+ *
+ * The authors hereby grant permission to use, copy, modify,
+ * distribute, and license this software and its documentation for any
+ * purpose, provided that existing copyright notices are retained in
+ * all copies and that this notice is included verbatim in any
+ * distributions. No written agreement, license, or royalty fee is
+ * required for any of the authorized uses. Modifications to this
+ * software may be copyrighted by their authors and need not follow
+ * the licensing terms described here, provided that the new terms are
+ * clearly indicated on the first page of each file where they apply.
+ *
+ * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+ * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
+ * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
+ * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
+ * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
+ * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * GOVERNMENT USE: If you are acquiring this software on behalf of the
+ * U.S. government, the Government shall have only "Restricted Rights"
+ * in the software and related documentation as defined in the Federal
+ * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
+ * are acquiring the software on behalf of the Department of Defense,
+ * the software shall be classified as "Commercial Computer Software"
+ * and the Government shall have only "Restricted Rights" as defined
+ * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
+ * foregoing, the authors grant the U.S. Government and others acting
+ * in its behalf permission to use and distribute the software in
+ * accordance with the terms specified in this license.
+ *
+ */
+
+#include "gtktexttag.h"
+#include "gtktexttypes.h"
+#include "gtktexttagtable.h"
+#include "gtksignal.h"
+
+#include <stdlib.h>
+
+enum {
+ EVENT,
+ LAST_SIGNAL
+};
+
+enum {
+ ARG_0,
+ /* Construct args */
+ ARG_NAME,
+
+ /* Style args */
+ ARG_BACKGROUND,
+ ARG_FOREGROUND,
+ ARG_BACKGROUND_GDK,
+ ARG_FOREGROUND_GDK,
+ ARG_BACKGROUND_STIPPLE,
+ ARG_FOREGROUND_STIPPLE,
+ ARG_FONT,
+ ARG_FONT_DESC,
+ ARG_PIXELS_ABOVE_LINES,
+ ARG_PIXELS_BELOW_LINES,
+ ARG_PIXELS_INSIDE_WRAP,
+ ARG_EDITABLE,
+ ARG_WRAP_MODE,
+ ARG_JUSTIFY,
+ ARG_DIRECTION,
+ ARG_LEFT_MARGIN,
+ ARG_LEFT_WRAPPED_LINE_MARGIN,
+ ARG_OVERSTRIKE,
+ ARG_RIGHT_MARGIN,
+ ARG_UNDERLINE,
+ ARG_OFFSET,
+ ARG_BG_FULL_HEIGHT,
+
+ /* Whether-a-style-arg-is-set args */
+ ARG_BACKGROUND_SET,
+ ARG_FOREGROUND_SET,
+ ARG_BACKGROUND_GDK_SET,
+ ARG_FOREGROUND_GDK_SET,
+ ARG_BACKGROUND_STIPPLE_SET,
+ ARG_FOREGROUND_STIPPLE_SET,
+ ARG_FONT_SET,
+ ARG_PIXELS_ABOVE_LINES_SET,
+ ARG_PIXELS_BELOW_LINES_SET,
+ ARG_PIXELS_INSIDE_WRAP_SET,
+ ARG_EDITABLE_SET,
+ ARG_WRAP_MODE_SET,
+ ARG_JUSTIFY_SET,
+ ARG_LEFT_MARGIN_SET,
+ ARG_LEFT_WRAPPED_LINE_MARGIN_SET,
+ ARG_OVERSTRIKE_SET,
+ ARG_RIGHT_MARGIN_SET,
+ ARG_UNDERLINE_SET,
+ ARG_OFFSET_SET,
+ ARG_BG_FULL_HEIGHT_SET,
+
+ LAST_ARG
+};
+
+static void gtk_text_tag_init (GtkTextTag *tkxt_tag);
+static void gtk_text_tag_class_init (GtkTextTagClass *klass);
+static void gtk_text_tag_destroy (GtkObject *object);
+static void gtk_text_tag_finalize (GObject *object);
+static void gtk_text_tag_set_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id);
+static void gtk_text_tag_get_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id);
+
+static GtkObjectClass *parent_class = NULL;
+static guint signals[LAST_SIGNAL] = { 0 };
+
+GtkType
+gtk_text_tag_get_type (void)
+{
+ static GtkType our_type = 0;
+
+ if (our_type == 0)
+ {
+ static const GtkTypeInfo our_info =
+ {
+ "GtkTextTag",
+ sizeof (GtkTextTag),
+ sizeof (GtkTextTagClass),
+ (GtkClassInitFunc) gtk_text_tag_class_init,
+ (GtkObjectInitFunc) gtk_text_tag_init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL
+ };
+
+ our_type = gtk_type_unique (GTK_TYPE_OBJECT, &our_info);
+ }
+
+ return our_type;
+}
+
+static void
+gtk_text_tag_class_init (GtkTextTagClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
+
+ parent_class = gtk_type_class (GTK_TYPE_OBJECT);
+
+ /* Construct */
+ gtk_object_add_arg_type ("GtkTextTag::name", GTK_TYPE_STRING,
+ GTK_ARG_READWRITE | GTK_ARG_CONSTRUCT_ONLY,
+ ARG_NAME);
+
+ /* Style args */
+ gtk_object_add_arg_type ("GtkTextTag::background", GTK_TYPE_STRING,
+ GTK_ARG_WRITABLE, ARG_BACKGROUND);
+ gtk_object_add_arg_type ("GtkTextTag::foreground", GTK_TYPE_STRING,
+ GTK_ARG_WRITABLE, ARG_FOREGROUND);
+ gtk_object_add_arg_type ("GtkTextTag::background_gdk", GTK_TYPE_GDK_COLOR,
+ GTK_ARG_READWRITE, ARG_BACKGROUND_GDK);
+ gtk_object_add_arg_type ("GtkTextTag::foreground_gdk", GTK_TYPE_GDK_COLOR,
+ GTK_ARG_READWRITE, ARG_FOREGROUND_GDK);
+ /* FIXME should be GTK_TYPE_GDK_BITMAP but that doesn't exist? */
+ gtk_object_add_arg_type ("GtkTextTag::background_stipple", GTK_TYPE_GDK_WINDOW,
+ GTK_ARG_READWRITE, ARG_BACKGROUND_STIPPLE);
+ /* FIXME GDK_BITMAP */
+ gtk_object_add_arg_type ("GtkTextTag::foreground_stipple", GTK_TYPE_GDK_WINDOW,
+ GTK_ARG_READWRITE, ARG_FOREGROUND_STIPPLE);
+ gtk_object_add_arg_type ("GtkTextTag::font", GTK_TYPE_STRING,
+ GTK_ARG_READWRITE, ARG_FONT);
+ gtk_object_add_arg_type ("GtkTextTag::font_desc", GTK_TYPE_BOXED,
+ GTK_ARG_READWRITE, ARG_FONT_DESC);
+ gtk_object_add_arg_type ("GtkTextTag::pixels_above_lines", GTK_TYPE_INT,
+ GTK_ARG_READWRITE, ARG_PIXELS_ABOVE_LINES);
+ gtk_object_add_arg_type ("GtkTextTag::pixels_below_lines", GTK_TYPE_INT,
+ GTK_ARG_READWRITE, ARG_PIXELS_BELOW_LINES);
+ gtk_object_add_arg_type ("GtkTextTag::pixels_inside_wrap", GTK_TYPE_INT,
+ GTK_ARG_READWRITE, ARG_PIXELS_INSIDE_WRAP);
+ gtk_object_add_arg_type ("GtkTextTag::editable", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_EDITABLE);
+ gtk_object_add_arg_type ("GtkTextTag::wrap_mode", GTK_TYPE_ENUM,
+ GTK_ARG_READWRITE, ARG_WRAP_MODE);
+ gtk_object_add_arg_type ("GtkTextTag::justify", GTK_TYPE_ENUM,
+ GTK_ARG_READWRITE, ARG_JUSTIFY);
+ gtk_object_add_arg_type ("GtkTextTag::direction", GTK_TYPE_ENUM,
+ GTK_ARG_READWRITE, ARG_DIRECTION);
+ gtk_object_add_arg_type ("GtkTextTag::left_margin", GTK_TYPE_INT,
+ GTK_ARG_READWRITE, ARG_LEFT_MARGIN);
+ gtk_object_add_arg_type ("GtkTextTag::left_wrapped_line_margin", GTK_TYPE_INT,
+ GTK_ARG_READWRITE, ARG_LEFT_WRAPPED_LINE_MARGIN);
+ gtk_object_add_arg_type ("GtkTextTag::overstrike", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_OVERSTRIKE);
+ gtk_object_add_arg_type ("GtkTextTag::right_margin", GTK_TYPE_INT,
+ GTK_ARG_READWRITE, ARG_RIGHT_MARGIN);
+ gtk_object_add_arg_type ("GtkTextTag::pixels_above_lines", GTK_TYPE_INT,
+ GTK_ARG_READWRITE, ARG_PIXELS_ABOVE_LINES);
+ gtk_object_add_arg_type ("GtkTextTag::pixels_below_lines", GTK_TYPE_INT,
+ GTK_ARG_READWRITE, ARG_PIXELS_BELOW_LINES);
+ gtk_object_add_arg_type ("GtkTextTag::pixels_inside_wrap", GTK_TYPE_INT,
+ GTK_ARG_READWRITE, ARG_PIXELS_INSIDE_WRAP);
+ gtk_object_add_arg_type ("GtkTextTag::underline", GTK_TYPE_ENUM,
+ GTK_ARG_READWRITE, ARG_UNDERLINE);
+ gtk_object_add_arg_type ("GtkTextTag::wrap_mode", GTK_TYPE_ENUM,
+ GTK_ARG_READWRITE, ARG_WRAP_MODE);
+ gtk_object_add_arg_type ("GtkTextTag::offset", GTK_TYPE_INT,
+ GTK_ARG_READWRITE, ARG_OFFSET);
+ gtk_object_add_arg_type ("GtkTextTag::background_full_height", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_BG_FULL_HEIGHT);
+
+ /* Style args are set or not */
+ gtk_object_add_arg_type ("GtkTextTag::background_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_BACKGROUND_SET);
+ gtk_object_add_arg_type ("GtkTextTag::foreground_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_FOREGROUND_SET);
+ gtk_object_add_arg_type ("GtkTextTag::background_gdk_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_BACKGROUND_GDK_SET);
+ gtk_object_add_arg_type ("GtkTextTag::foreground_gdk_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_FOREGROUND_GDK_SET);
+ gtk_object_add_arg_type ("GtkTextTag::background_stipple_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_BACKGROUND_STIPPLE_SET);
+ gtk_object_add_arg_type ("GtkTextTag::foreground_stipple_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_FOREGROUND_STIPPLE_SET);
+ gtk_object_add_arg_type ("GtkTextTag::font_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_FONT_SET);
+ gtk_object_add_arg_type ("GtkTextTag::pixels_above_lines_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_PIXELS_ABOVE_LINES_SET);
+ gtk_object_add_arg_type ("GtkTextTag::pixels_below_lines_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_PIXELS_BELOW_LINES_SET);
+ gtk_object_add_arg_type ("GtkTextTag::pixels_inside_wrap_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_PIXELS_INSIDE_WRAP_SET);
+ gtk_object_add_arg_type ("GtkTextTag::editable_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_EDITABLE_SET);
+ gtk_object_add_arg_type ("GtkTextTag::wrap_mode_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_WRAP_MODE_SET);
+ gtk_object_add_arg_type ("GtkTextTag::justify_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_JUSTIFY_SET);
+ gtk_object_add_arg_type ("GtkTextTag::left_margin_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_LEFT_MARGIN_SET);
+ gtk_object_add_arg_type ("GtkTextTag::left_wrapped_line_margin_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_LEFT_WRAPPED_LINE_MARGIN_SET);
+ gtk_object_add_arg_type ("GtkTextTag::overstrike_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_OVERSTRIKE_SET);
+ gtk_object_add_arg_type ("GtkTextTag::right_margin_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_RIGHT_MARGIN_SET);
+ gtk_object_add_arg_type ("GtkTextTag::pixels_above_lines_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_PIXELS_ABOVE_LINES_SET);
+ gtk_object_add_arg_type ("GtkTextTag::pixels_below_lines_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_PIXELS_BELOW_LINES_SET);
+ gtk_object_add_arg_type ("GtkTextTag::pixels_inside_wrap_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_PIXELS_INSIDE_WRAP_SET);
+ gtk_object_add_arg_type ("GtkTextTag::underline_set", GTK_TYPE_ENUM,
+ GTK_ARG_READWRITE, ARG_UNDERLINE_SET);
+ gtk_object_add_arg_type ("GtkTextTag::wrap_mode_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_WRAP_MODE_SET);
+ gtk_object_add_arg_type ("GtkTextTag::offset_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_OFFSET_SET);
+ gtk_object_add_arg_type ("GtkTextTag::background_full_height_set", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_BG_FULL_HEIGHT_SET);
+
+ signals[EVENT] =
+ gtk_signal_new ("event",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextTagClass, event),
+ gtk_marshal_INT__OBJECT_BOXED_POINTER,
+ GTK_TYPE_INT,
+ 3,
+ GTK_TYPE_OBJECT,
+ GTK_TYPE_GDK_EVENT,
+ GTK_TYPE_POINTER);
+
+ gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+
+ object_class->set_arg = gtk_text_tag_set_arg;
+ object_class->get_arg = gtk_text_tag_get_arg;
+
+ object_class->destroy = gtk_text_tag_destroy;
+ gobject_class->finalize = gtk_text_tag_finalize;
+}
+
+void
+gtk_text_tag_init (GtkTextTag *tkxt_tag)
+{
+ /* 0 is basically a fine way to initialize everything in the
+ entire struct */
+
+}
+
+GtkTextTag*
+gtk_text_tag_new (const gchar *name)
+{
+ GtkTextTag *tag;
+
+ tag = GTK_TEXT_TAG (gtk_type_new (gtk_text_tag_get_type ()));
+
+ tag->name = g_strdup(name);
+
+ tag->values = gtk_text_view_style_values_new();
+
+ return tag;
+}
+
+static void
+gtk_text_tag_destroy (GtkObject *object)
+{
+ GtkTextTag *tkxt_tag;
+
+ tkxt_tag = GTK_TEXT_TAG (object);
+
+ g_assert(!tkxt_tag->values->realized);
+
+ if (tkxt_tag->table)
+ gtk_text_tag_table_remove(tkxt_tag->table, tkxt_tag->name);
+
+ g_assert(tkxt_tag->table == NULL);
+
+ gtk_text_view_style_values_unref(tkxt_tag->values);
+ tkxt_tag->values = NULL;
+
+ (* GTK_OBJECT_CLASS(parent_class)->destroy) (object);
+}
+
+static void
+gtk_text_tag_finalize (GObject *object)
+{
+ GtkTextTag *tkxt_tag;
+
+ tkxt_tag = GTK_TEXT_TAG (object);
+
+ g_free(tkxt_tag->name);
+ tkxt_tag->name = NULL;
+
+ (* G_OBJECT_CLASS(parent_class)->finalize) (object);
+}
+
+static void
+set_bg_color(GtkTextTag *tag, GdkColor *color)
+{
+ if (color)
+ {
+ tag->bg_color_set = TRUE;
+ tag->values->appearance.bg_color = *color;
+ }
+ else
+ {
+ tag->bg_color_set = FALSE;
+ }
+}
+
+static void
+set_fg_color(GtkTextTag *tag, GdkColor *color)
+{
+ if (color)
+ {
+ tag->fg_color_set = TRUE;
+ tag->values->appearance.fg_color = *color;
+ }
+ else
+ {
+ tag->fg_color_set = FALSE;
+ }
+}
+
+static void
+gtk_text_tag_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ GtkTextTag *tkxt_tag;
+ gboolean size_changed = FALSE;
+
+ tkxt_tag = GTK_TEXT_TAG (object);
+
+ g_return_if_fail(!tkxt_tag->values->realized);
+
+ switch (arg_id)
+ {
+ case ARG_NAME:
+ g_return_if_fail(tkxt_tag->name == NULL);
+ tkxt_tag->name = g_strdup(GTK_VALUE_STRING(*arg));
+ break;
+
+ case ARG_BACKGROUND:
+ {
+ GdkColor color;
+
+ if (gdk_color_parse(GTK_VALUE_STRING(*arg), &color))
+ set_bg_color(tkxt_tag, &color);
+ else
+ g_warning("Don't know color `%s'", GTK_VALUE_STRING(*arg));
+ }
+ break;
+
+ case ARG_FOREGROUND:
+ {
+ GdkColor color;
+
+ if (gdk_color_parse(GTK_VALUE_STRING(*arg), &color))
+ set_fg_color(tkxt_tag, &color);
+ else
+ g_warning("Don't know color `%s'", GTK_VALUE_STRING(*arg));
+ }
+ break;
+
+ case ARG_BACKGROUND_GDK:
+ {
+ GdkColor *color = GTK_VALUE_POINTER(*arg);
+ set_bg_color(tkxt_tag, color);
+ }
+ break;
+
+ case ARG_FOREGROUND_GDK:
+ {
+ GdkColor *color = GTK_VALUE_POINTER(*arg);
+ set_fg_color(tkxt_tag, color);
+ }
+ break;
+
+ case ARG_BACKGROUND_STIPPLE:
+ {
+ GdkBitmap *bitmap = GTK_VALUE_POINTER(*arg);
+
+ tkxt_tag->bg_stipple_set = TRUE;
+
+ if (tkxt_tag->values->appearance.bg_stipple != bitmap)
+ {
+ if (bitmap != NULL)
+ gdk_bitmap_ref(bitmap);
+
+ if (tkxt_tag->values->appearance.bg_stipple)
+ gdk_bitmap_unref(tkxt_tag->values->appearance.bg_stipple);
+
+ tkxt_tag->values->appearance.bg_stipple = bitmap;
+ }
+ }
+ break;
+
+ case ARG_FOREGROUND_STIPPLE:
+ {
+ GdkBitmap *bitmap = GTK_VALUE_POINTER(*arg);
+
+ tkxt_tag->fg_stipple_set = TRUE;
+
+ if (tkxt_tag->values->appearance.fg_stipple != bitmap)
+ {
+ if (bitmap != NULL)
+ gdk_bitmap_ref(bitmap);
+
+ if (tkxt_tag->values->appearance.fg_stipple)
+ gdk_bitmap_unref(tkxt_tag->values->appearance.fg_stipple);
+
+ tkxt_tag->values->appearance.fg_stipple = bitmap;
+ }
+ }
+ break;
+
+ case ARG_FONT:
+ {
+ PangoFontDescription *font_desc = NULL;
+ const gchar *name;
+
+ name = GTK_VALUE_STRING(*arg);
+
+ if (name)
+ font_desc = pango_font_description_from_string (name);
+
+ if (tkxt_tag->values->font_desc)
+ pango_font_description_free (tkxt_tag->values->font_desc);
+
+ tkxt_tag->font_set = (font_desc != NULL);
+ tkxt_tag->values->font_desc = font_desc;
+
+ size_changed = TRUE;
+ }
+ break;
+
+ case ARG_FONT_DESC:
+ {
+ PangoFontDescription *font_desc;
+
+ font_desc = GTK_VALUE_BOXED(*arg);
+
+ if (tkxt_tag->values->font_desc)
+ pango_font_description_free (tkxt_tag->values->font_desc);
+
+ if (font_desc)
+ tkxt_tag->values->font_desc = pango_font_description_copy (font_desc);
+ else
+ tkxt_tag->values->font_desc = NULL;
+
+ tkxt_tag->font_set = (font_desc != NULL);
+
+ size_changed = TRUE;
+ }
+ break;
+
+ case ARG_PIXELS_ABOVE_LINES:
+ tkxt_tag->pixels_above_lines_set = TRUE;
+ tkxt_tag->values->pixels_above_lines = GTK_VALUE_INT(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_PIXELS_BELOW_LINES:
+ tkxt_tag->pixels_below_lines_set = TRUE;
+ tkxt_tag->values->pixels_below_lines = GTK_VALUE_INT(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_PIXELS_INSIDE_WRAP:
+ tkxt_tag->pixels_inside_wrap_set = TRUE;
+ tkxt_tag->values->pixels_inside_wrap = GTK_VALUE_INT(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_EDITABLE:
+ tkxt_tag->editable_set = TRUE;
+ tkxt_tag->values->editable = GTK_VALUE_BOOL(*arg);
+ break;
+
+ case ARG_WRAP_MODE:
+ tkxt_tag->wrap_mode_set = TRUE;
+ tkxt_tag->values->wrap_mode = GTK_VALUE_ENUM(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_JUSTIFY:
+ tkxt_tag->justify_set = TRUE;
+ tkxt_tag->values->justify = GTK_VALUE_ENUM(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_DIRECTION:
+ tkxt_tag->values->direction = GTK_VALUE_ENUM(*arg);
+ break;
+
+ case ARG_LEFT_MARGIN:
+ tkxt_tag->left_margin_set = TRUE;
+ tkxt_tag->values->left_margin = GTK_VALUE_INT(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_LEFT_WRAPPED_LINE_MARGIN:
+ tkxt_tag->left_wrapped_line_margin_set = TRUE;
+ tkxt_tag->values->left_wrapped_line_margin = GTK_VALUE_INT(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_OVERSTRIKE:
+ tkxt_tag->overstrike_set = TRUE;
+ tkxt_tag->values->appearance.overstrike = GTK_VALUE_BOOL(*arg);
+ break;
+
+ case ARG_RIGHT_MARGIN:
+ tkxt_tag->right_margin_set = TRUE;
+ tkxt_tag->values->right_margin = GTK_VALUE_INT(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_UNDERLINE:
+ tkxt_tag->underline_set = TRUE;
+ tkxt_tag->values->appearance.underline = GTK_VALUE_ENUM(*arg);
+ break;
+
+ case ARG_OFFSET:
+ tkxt_tag->offset_set = TRUE;
+ tkxt_tag->values->offset = GTK_VALUE_INT(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_BG_FULL_HEIGHT:
+ tkxt_tag->bg_full_height_set = TRUE;
+ tkxt_tag->values->bg_full_height = GTK_VALUE_BOOL(*arg);
+ break;
+
+
+ /* Whether the value should be used... */
+
+ case ARG_BACKGROUND_SET:
+ case ARG_BACKGROUND_GDK_SET:
+ tkxt_tag->bg_color_set = GTK_VALUE_BOOL(*arg);
+ break;
+
+ case ARG_FOREGROUND_SET:
+ case ARG_FOREGROUND_GDK_SET:
+ tkxt_tag->fg_color_set = GTK_VALUE_BOOL(*arg);
+ break;
+
+ case ARG_BACKGROUND_STIPPLE_SET:
+ tkxt_tag->bg_stipple_set = GTK_VALUE_BOOL(*arg);
+ break;
+
+ case ARG_FOREGROUND_STIPPLE_SET:
+ tkxt_tag->fg_stipple_set = GTK_VALUE_BOOL(*arg);
+ break;
+
+ case ARG_FONT_SET:
+ tkxt_tag->font_set = GTK_VALUE_BOOL(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_PIXELS_ABOVE_LINES_SET:
+ tkxt_tag->pixels_above_lines_set = GTK_VALUE_BOOL(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_PIXELS_BELOW_LINES_SET:
+ tkxt_tag->pixels_below_lines_set = GTK_VALUE_BOOL(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_PIXELS_INSIDE_WRAP_SET:
+ tkxt_tag->pixels_inside_wrap_set = GTK_VALUE_BOOL(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_EDITABLE_SET:
+ tkxt_tag->editable_set = GTK_VALUE_BOOL(*arg);
+ break;
+
+ case ARG_WRAP_MODE_SET:
+ tkxt_tag->wrap_mode_set = GTK_VALUE_BOOL(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_JUSTIFY_SET:
+ tkxt_tag->justify_set = GTK_VALUE_BOOL(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_LEFT_MARGIN_SET:
+ tkxt_tag->left_margin_set = GTK_VALUE_BOOL(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_LEFT_WRAPPED_LINE_MARGIN_SET:
+ tkxt_tag->left_wrapped_line_margin_set = GTK_VALUE_BOOL(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_OVERSTRIKE_SET:
+ tkxt_tag->overstrike_set = GTK_VALUE_BOOL(*arg);
+ break;
+
+ case ARG_RIGHT_MARGIN_SET:
+ tkxt_tag->right_margin_set = GTK_VALUE_BOOL(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_UNDERLINE_SET:
+ tkxt_tag->underline_set = GTK_VALUE_BOOL(*arg);
+ break;
+
+ case ARG_OFFSET_SET:
+ tkxt_tag->offset_set = GTK_VALUE_BOOL(*arg);
+ size_changed = TRUE;
+ break;
+
+ case ARG_BG_FULL_HEIGHT_SET:
+ tkxt_tag->bg_full_height_set = TRUE;
+ tkxt_tag->values->bg_full_height = GTK_VALUE_BOOL(*arg);
+ break;
+
+ default:
+ g_assert_not_reached();
+ break;
+ }
+
+ /* FIXME I would like to do this after all set_arg in a single
+ gtk_object_set() have been called. But an idle function
+ won't work; we need to emit when the tag is changed, not
+ when we get around to the event loop. So blah, we eat some
+ inefficiency. */
+
+ /* This is also somewhat weird since we emit another object's
+ signal here, but the two objects are already tightly bound. */
+
+ if (tkxt_tag->table)
+ gtk_signal_emit_by_name(GTK_OBJECT(tkxt_tag->table),
+ "tag_changed",
+ tkxt_tag, size_changed);
+}
+
+static void
+get_color_arg (GtkArg *arg, GdkColor *orig)
+{
+ GdkColor *color;
+
+ color = g_new (GdkColor, 1);
+ *color = *orig;
+ GTK_VALUE_BOXED (*arg) = color;
+}
+
+static void
+gtk_text_tag_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ GtkTextTag *tag;
+
+ tag = GTK_TEXT_TAG (object);
+
+ switch (arg_id)
+ {
+ case ARG_NAME:
+ GTK_VALUE_STRING(*arg) = g_strdup(tag->name);
+ break;
+
+ case ARG_BACKGROUND_GDK:
+ get_color_arg(arg, &tag->values->appearance.bg_color);
+ break;
+
+ case ARG_FOREGROUND_GDK:
+ get_color_arg(arg, &tag->values->appearance.fg_color);
+ break;
+
+ case ARG_BACKGROUND_STIPPLE:
+ GTK_VALUE_BOXED(*arg) = tag->values->appearance.bg_stipple;
+ break;
+
+ case ARG_FOREGROUND_STIPPLE:
+ GTK_VALUE_BOXED(*arg) = tag->values->appearance.fg_stipple;
+ break;
+
+ case ARG_FONT:
+ if (tag->values->font_desc)
+ GTK_VALUE_STRING(*arg) = pango_font_description_to_string (tag->values->font_desc);
+ else
+ GTK_VALUE_STRING(*arg) = NULL;
+ break;
+
+ case ARG_FONT_DESC:
+ if (tag->values->font_desc)
+ GTK_VALUE_BOXED(*arg) = pango_font_description_copy (tag->values->font_desc);
+ else
+ GTK_VALUE_BOXED(*arg) = NULL;
+ break;
+
+ case ARG_PIXELS_ABOVE_LINES:
+ GTK_VALUE_INT(*arg) = tag->values->pixels_above_lines;
+ break;
+
+ case ARG_PIXELS_BELOW_LINES:
+ GTK_VALUE_INT(*arg) = tag->values->pixels_below_lines;
+ break;
+
+ case ARG_PIXELS_INSIDE_WRAP:
+ GTK_VALUE_INT(*arg) = tag->values->pixels_inside_wrap;
+ break;
+
+ case ARG_EDITABLE:
+ GTK_VALUE_BOOL(*arg) = tag->values->editable;
+ break;
+
+ case ARG_WRAP_MODE:
+ GTK_VALUE_ENUM(*arg) = tag->values->wrap_mode;
+ break;
+
+ case ARG_JUSTIFY:
+ GTK_VALUE_ENUM(*arg) = tag->values->justify;
+ break;
+
+ case ARG_LEFT_MARGIN:
+ GTK_VALUE_INT(*arg) = tag->values->left_margin;
+ break;
+
+ case ARG_LEFT_WRAPPED_LINE_MARGIN:
+ GTK_VALUE_INT(*arg) = tag->values->left_wrapped_line_margin;
+ break;
+
+ case ARG_OVERSTRIKE:
+ GTK_VALUE_BOOL(*arg) = tag->values->appearance.overstrike;
+ break;
+
+ case ARG_RIGHT_MARGIN:
+ GTK_VALUE_INT(*arg) = tag->values->right_margin;
+ break;
+
+ case ARG_UNDERLINE:
+ GTK_VALUE_ENUM(*arg) = tag->values->appearance.underline;
+ break;
+
+ case ARG_OFFSET:
+ GTK_VALUE_INT(*arg) = tag->values->offset;
+ break;
+
+ case ARG_BG_FULL_HEIGHT:
+ GTK_VALUE_BOOL(*arg) = tag->values->bg_full_height;
+ break;
+
+ case ARG_BACKGROUND_SET:
+ case ARG_BACKGROUND_GDK_SET:
+ GTK_VALUE_BOOL(*arg) = tag->bg_color_set;
+ break;
+
+ case ARG_FOREGROUND_SET:
+ case ARG_FOREGROUND_GDK_SET:
+ GTK_VALUE_BOOL(*arg) = tag->fg_color_set;
+ break;
+
+ case ARG_BACKGROUND_STIPPLE_SET:
+ GTK_VALUE_BOOL(*arg) = tag->bg_stipple_set;
+ break;
+
+ case ARG_FOREGROUND_STIPPLE_SET:
+ GTK_VALUE_BOOL(*arg) = tag->fg_stipple_set;
+ break;
+
+ case ARG_FONT_SET:
+ GTK_VALUE_BOOL(*arg) = tag->font_set;
+ break;
+
+ case ARG_PIXELS_ABOVE_LINES_SET:
+ GTK_VALUE_BOOL(*arg) = tag->pixels_above_lines_set;
+ break;
+
+ case ARG_PIXELS_BELOW_LINES_SET:
+ GTK_VALUE_BOOL(*arg) = tag->pixels_below_lines_set;
+ break;
+
+ case ARG_PIXELS_INSIDE_WRAP_SET:
+ GTK_VALUE_BOOL(*arg) = tag->pixels_inside_wrap_set;
+ break;
+
+ case ARG_EDITABLE_SET:
+ GTK_VALUE_BOOL(*arg) = tag->editable_set;
+ break;
+
+ case ARG_WRAP_MODE_SET:
+ GTK_VALUE_BOOL(*arg) = tag->wrap_mode_set;
+ break;
+
+ case ARG_JUSTIFY_SET:
+ GTK_VALUE_BOOL(*arg) = tag->justify_set;
+ break;
+
+ case ARG_LEFT_MARGIN_SET:
+ GTK_VALUE_BOOL(*arg) = tag->left_margin_set;
+ break;
+
+ case ARG_LEFT_WRAPPED_LINE_MARGIN_SET:
+ GTK_VALUE_BOOL(*arg) = tag->left_wrapped_line_margin_set;
+ break;
+
+ case ARG_OVERSTRIKE_SET:
+ GTK_VALUE_BOOL(*arg) = tag->overstrike_set;
+ break;
+
+ case ARG_RIGHT_MARGIN_SET:
+ GTK_VALUE_BOOL(*arg) = tag->right_margin_set;
+ break;
+
+ case ARG_UNDERLINE_SET:
+ GTK_VALUE_BOOL(*arg) = tag->underline_set;
+ break;
+
+ case ARG_OFFSET_SET:
+ GTK_VALUE_BOOL(*arg) = tag->offset_set;
+ break;
+
+ case ARG_BG_FULL_HEIGHT_SET:
+ GTK_VALUE_BOOL(*arg) = tag->bg_full_height_set;
+ break;
+
+ case ARG_BACKGROUND:
+ case ARG_FOREGROUND:
+ default:
+ arg->type = GTK_TYPE_INVALID;
+ break;
+ }
+ /* FIXME */
+ arg->type = GTK_TYPE_INVALID;
+}
+
+/*
+ * Tag operations
+ */
+
+typedef struct {
+ gint high;
+ gint low;
+ gint delta;
+} DeltaData;
+
+static void
+delta_priority_foreach(gpointer key, gpointer value, gpointer user_data)
+{
+ GtkTextTag *tag;
+ DeltaData *dd = user_data;
+
+ tag = GTK_TEXT_TAG(value);
+
+ if (tag->priority >= dd->low && tag->priority <= dd->high)
+ tag->priority += dd->delta;
+}
+
+void
+gtk_text_tag_set_priority(GtkTextTag *tag,
+ gint priority)
+{
+ DeltaData dd;
+
+ g_return_if_fail(GTK_IS_TEXT_TAG(tag));
+ g_return_if_fail(tag->table != NULL);
+ g_return_if_fail(priority >= 0);
+ g_return_if_fail(priority < gtk_text_tag_table_size(tag->table));
+
+ if (priority == tag->priority)
+ return;
+
+ if (priority < tag->priority) {
+ dd.low = priority;
+ dd.high = tag->priority - 1;
+ dd.delta = 1;
+ } else {
+ dd.low = tag->priority + 1;
+ dd.high = priority;
+ dd.delta = -1;
+ }
+
+ gtk_text_tag_table_foreach(tag->table, delta_priority_foreach,
+ &dd);
+
+ tag->priority = priority;
+}
+
+gint
+gtk_text_tag_event(GtkTextTag *tag,
+ GtkObject *event_object,
+ GdkEvent *event,
+ const GtkTextIter *iter)
+{
+ gint retval = FALSE;
+
+ g_return_val_if_fail(GTK_IS_TEXT_TAG(tag), FALSE);
+ g_return_val_if_fail(GTK_IS_OBJECT(event_object), FALSE);
+ g_return_val_if_fail(event != NULL, FALSE);
+
+ gtk_signal_emit(GTK_OBJECT(tag),
+ signals[EVENT],
+ event_object,
+ event,
+ iter,
+ &retval);
+
+ return retval;
+}
+
+static int
+tag_sort_func(gconstpointer first, gconstpointer second)
+{
+ GtkTextTag *tag1, *tag2;
+
+ tag1 = * (GtkTextTag **) first;
+ tag2 = * (GtkTextTag **) second;
+ return tag1->priority - tag2->priority;
+}
+
+void
+gtk_text_tag_array_sort(GtkTextTag** tag_array_p,
+ guint len)
+{
+ int i, j, prio;
+ GtkTextTag **tag;
+ GtkTextTag **maxPtrPtr, *tmp;
+
+ g_return_if_fail(tag_array_p != NULL);
+ g_return_if_fail(len > 0);
+
+ if (len < 2) {
+ return;
+ }
+ if (len < 20) {
+ GtkTextTag **iter = tag_array_p;
+
+ for (i = len-1; i > 0; i--, iter++) {
+ maxPtrPtr = tag = iter;
+ prio = tag[0]->priority;
+ for (j = i, tag++; j > 0; j--, tag++) {
+ if (tag[0]->priority < prio) {
+ prio = tag[0]->priority;
+ maxPtrPtr = tag;
+ }
+ }
+ tmp = *maxPtrPtr;
+ *maxPtrPtr = *iter;
+ *iter = tmp;
+ }
+ } else {
+ qsort((void *) tag_array_p, (unsigned) len, sizeof (GtkTextTag *),
+ tag_sort_func);
+ }
+
+#if 0
+ {
+ printf("Sorted tag array: \n");
+ i = 0;
+ while (i < len)
+ {
+ GtkTextTag *t = tag_array_p[i];
+ printf(" %s priority %d\n", t->name, t->priority);
+
+ ++i;
+ }
+ }
+#endif
+}
+
+/*
+ * StyleValues
+ */
+
+GtkTextStyleValues*
+gtk_text_view_style_values_new(void)
+{
+ GtkTextStyleValues *values;
+
+ values = g_new0(GtkTextStyleValues, 1);
+
+ /* 0 is a valid value for most of the struct */
+
+ values->refcount = 1;
+
+ return values;
+}
+
+void
+gtk_text_view_style_values_copy(GtkTextStyleValues *src,
+ GtkTextStyleValues *dest)
+{
+ guint orig_refcount;
+
+ g_return_if_fail(!dest->realized);
+
+ if (src == dest)
+ return;
+
+ /* Add refs */
+
+ if (src->appearance.bg_stipple)
+ gdk_bitmap_ref(src->appearance.bg_stipple);
+
+ if (src->appearance.fg_stipple)
+ gdk_bitmap_ref(src->appearance.fg_stipple);
+
+ if (src->tab_array)
+ gtk_text_view_tab_array_ref(src->tab_array);
+
+ /* Remove refs */
+
+ if (dest->appearance.bg_stipple)
+ gdk_bitmap_unref(dest->appearance.bg_stipple);
+
+ if (dest->appearance.fg_stipple)
+ gdk_bitmap_unref(dest->appearance.fg_stipple);
+
+ if (dest->tab_array)
+ gtk_text_view_tab_array_unref(dest->tab_array);
+
+ /* Copy */
+ orig_refcount = dest->refcount;
+
+ *dest = *src;
+
+ dest->font_desc = pango_font_description_copy (src->font_desc);
+
+ dest->refcount = orig_refcount;
+ dest->realized = FALSE;
+}
+
+void
+gtk_text_view_style_values_ref(GtkTextStyleValues *values)
+{
+ g_return_if_fail(values != NULL);
+
+ values->refcount += 1;
+}
+
+void
+gtk_text_view_style_values_unref(GtkTextStyleValues *values)
+{
+ g_return_if_fail(values != NULL);
+ g_return_if_fail(values->refcount > 0);
+
+ values->refcount -= 1;
+
+ if (values->refcount == 0)
+ {
+ g_assert(!values->realized);
+
+ if (values->appearance.bg_stipple)
+ gdk_bitmap_unref(values->appearance.bg_stipple);
+
+ if (values->font_desc)
+ pango_font_description_free (values->font_desc);
+
+ if (values->appearance.fg_stipple)
+ gdk_bitmap_unref(values->appearance.fg_stipple);
+
+ if (values->tab_array)
+ gtk_text_view_tab_array_unref(values->tab_array);
+
+ g_free(values);
+ }
+}
+
+void
+gtk_text_view_style_values_realize(GtkTextStyleValues *values,
+ GdkColormap *cmap,
+ GdkVisual *visual)
+{
+ g_return_if_fail(values != NULL);
+ g_return_if_fail(values->refcount > 0);
+ g_return_if_fail(!values->realized);
+
+ /* It is wrong to use this colormap, FIXME */
+ gdk_colormap_alloc_color(cmap,
+ &values->appearance.fg_color,
+ FALSE, TRUE);
+
+ gdk_colormap_alloc_color(cmap,
+ &values->appearance.bg_color,
+ FALSE, TRUE);
+
+ values->realized = TRUE;
+}
+
+void
+gtk_text_view_style_values_unrealize(GtkTextStyleValues *values,
+ GdkColormap *cmap,
+ GdkVisual *visual)
+{
+ g_return_if_fail(values != NULL);
+ g_return_if_fail(values->refcount > 0);
+ g_return_if_fail(values->realized);
+
+ gdk_colormap_free_colors(cmap,
+ &values->appearance.fg_color, 1);
+
+
+ gdk_colormap_free_colors(cmap,
+ &values->appearance.bg_color, 1);
+
+ values->appearance.fg_color.pixel = 0;
+ values->appearance.bg_color.pixel = 0;
+
+ values->realized = FALSE;
+}
+
+void
+gtk_text_view_style_values_fill_from_tags(GtkTextStyleValues *dest,
+ GtkTextTag** tags,
+ guint n_tags)
+{
+ guint n = 0;
+
+ g_return_if_fail(!dest->realized);
+
+ while (n < n_tags)
+ {
+ GtkTextTag *tag = tags[n];
+ GtkTextStyleValues *vals = tag->values;
+
+ if (n > 0)
+ g_assert(tags[n]->priority > tags[n-1]->priority);
+
+ if (tag->bg_color_set)
+ {
+ dest->appearance.bg_color = vals->appearance.bg_color;
+
+ dest->appearance.draw_bg = TRUE;
+ }
+
+ if (tag->border_width_set)
+ dest->border_width = vals->border_width;
+
+ if (tag->relief_set)
+ dest->relief = vals->relief;
+
+ if (tag->bg_stipple_set)
+ {
+ gdk_bitmap_ref(vals->appearance.bg_stipple);
+ if (dest->appearance.bg_stipple)
+ gdk_bitmap_unref(dest->appearance.bg_stipple);
+ dest->appearance.bg_stipple = vals->appearance.bg_stipple;
+
+ dest->appearance.draw_bg = TRUE;
+ }
+
+ if (tag->fg_color_set)
+ dest->appearance.fg_color = vals->appearance.fg_color;
+
+ if (tag->font_set)
+ {
+ if (dest->font_desc)
+ pango_font_description_free (dest->font_desc);
+ dest->font_desc = pango_font_description_copy (vals->font_desc);
+ }
+
+ if (tag->fg_stipple_set)
+ {
+ gdk_bitmap_ref(vals->appearance.fg_stipple);
+ if (dest->appearance.fg_stipple)
+ gdk_bitmap_unref(dest->appearance.fg_stipple);
+ dest->appearance.fg_stipple = vals->appearance.fg_stipple;
+ }
+
+ if (tag->justify_set)
+ dest->justify = vals->justify;
+
+ if (vals->direction != GTK_TEXT_DIR_NONE)
+ dest->direction = vals->direction;
+
+ if (tag->left_margin_set)
+ dest->left_margin = vals->left_margin;
+
+ if (tag->left_wrapped_line_margin_set)
+ dest->left_wrapped_line_margin = vals->left_wrapped_line_margin;
+
+ if (tag->offset_set)
+ dest->offset = vals->offset;
+
+ if (tag->right_margin_set)
+ dest->right_margin = vals->right_margin;
+
+ if (tag->pixels_above_lines_set)
+ dest->pixels_above_lines = vals->pixels_above_lines;
+
+ if (tag->pixels_below_lines_set)
+ dest->pixels_below_lines = vals->pixels_below_lines;
+
+ if (tag->pixels_inside_wrap_set)
+ dest->pixels_inside_wrap = vals->pixels_inside_wrap;
+
+ if (tag->tab_array_set)
+ {
+ gtk_text_view_tab_array_ref(vals->tab_array);
+ if (dest->tab_array)
+ gtk_text_view_tab_array_unref(dest->tab_array);
+ dest->tab_array = vals->tab_array;
+ }
+
+ if (tag->wrap_mode_set)
+ dest->wrap_mode = vals->wrap_mode;
+
+ if (tag->underline_set)
+ dest->appearance.underline = vals->appearance.underline;
+
+ if (tag->overstrike_set)
+ dest->appearance.overstrike = vals->appearance.overstrike;
+
+ if (tag->elide_set)
+ dest->elide = vals->elide;
+
+ if (tag->editable_set)
+ dest->editable = vals->editable;
+
+ if (tag->bg_full_height_set)
+ dest->bg_full_height = vals->bg_full_height;
+
+ ++n;
+ }
+}
diff --git a/gtk/gtktexttag.h b/gtk/gtktexttag.h
new file mode 100644
index 000000000..c76b46fcc
--- /dev/null
+++ b/gtk/gtktexttag.h
@@ -0,0 +1,114 @@
+#ifndef GTK_TEXT_TAG_H
+#define GTK_TEXT_TAG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <gtk/gtkobject.h>
+#include <gdk/gdk.h>
+
+typedef struct _GtkTextIter GtkTextIter;
+typedef struct _GtkTextBTreeNode GtkTextBTreeNode;
+typedef struct _GtkTextTagTable GtkTextTagTable;
+typedef struct _GtkTextTabArray GtkTextTabArray;
+
+typedef enum {
+ GTK_WRAPMODE_NONE,
+ GTK_WRAPMODE_CHAR,
+ GTK_WRAPMODE_WORD
+} GtkWrapMode;
+
+typedef struct _GtkTextStyleValues GtkTextStyleValues;
+
+#define GTK_TYPE_TEXT_TAG (gtk_text_tag_get_type())
+#define GTK_TEXT_TAG(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_TEXT_TAG, GtkTextTag))
+#define GTK_TEXT_TAG_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TEXT_TAG, GtkTextTagClass))
+#define GTK_IS_TEXT_TAG(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_TEXT_TAG))
+#define GTK_IS_TEXT_TAG_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TEXT_TAG))
+#define GTK_TEXT_TAG_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_TEXT_TAG, GtkTextTagClass))
+
+typedef struct _GtkTextTag GtkTextTag;
+typedef struct _GtkTextTagClass GtkTextTagClass;
+
+struct _GtkTextTag {
+ GtkObject parent_instance;
+
+ GtkTextTagTable *table;
+
+ char *name; /* Name of this tag. This field is actually
+ * a pointer to the key from the entry in
+ * tkxt->tagTable, so it needn't be freed
+ * explicitly. */
+ int priority; /* Priority of this tag within widget. 0
+ * means lowest priority. Exactly one tag
+ * has each integer value between 0 and
+ * numTags-1. */
+ /*
+ * Information for displaying text with this tag. The information
+ * belows acts as an override on information specified by lower-priority
+ * tags. If no value is specified, then the next-lower-priority tag
+ * on the text determins the value. The text widget itself provides
+ * defaults if no tag specifies an override.
+ */
+
+ GtkTextStyleValues *values;
+
+ /* tag contains appearance-related options */
+ guint affects_size : 1;
+
+ /*
+ Flags for whether a given value is set; if a value is unset, then
+ this tag does not affect it. */
+ guint bg_color_set : 1;
+ guint border_width_set : 1;
+ guint relief_set : 1;
+ guint bg_stipple_set : 1;
+ guint fg_color_set : 1;
+ guint font_set : 1;
+ guint fg_stipple_set : 1;
+ guint justify_set : 1;
+ guint left_margin_set : 1;
+ guint left_wrapped_line_margin_set : 1;
+ guint offset_set : 1;
+ guint overstrike_set : 1;
+ guint right_margin_set : 1;
+ guint pixels_above_lines_set : 1;
+ guint pixels_below_lines_set : 1;
+ guint pixels_inside_wrap_set : 1;
+ guint tab_array_set : 1;
+ guint underline_set : 1;
+ guint wrap_mode_set : 1;
+ guint bg_full_height_set : 1;
+ guint elide_set : 1;
+ guint editable_set : 1;
+ guint pad1 : 1;
+ guint pad2 : 1;
+ guint pad3 : 1;
+ guint pad4 : 1;
+};
+
+struct _GtkTextTagClass {
+ GtkObjectClass parent_class;
+
+ gint (* event) (GtkTextTag *tag,
+ GtkObject *event_object, /* widget, canvas item, whatever */
+ GdkEvent *event, /* the event itself */
+ const GtkTextIter *iter); /* location of event in buffer */
+};
+
+GtkType gtk_text_tag_get_type (void);
+GtkTextTag *gtk_text_tag_new (const gchar *name);
+void gtk_text_tag_set_priority (GtkTextTag *tag,
+ gint priority);
+gint gtk_text_tag_event (GtkTextTag *tag,
+ GtkObject *event_object,
+ GdkEvent *event,
+ const GtkTextIter *iter);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
diff --git a/gtk/gtktexttagprivate.h b/gtk/gtktexttagprivate.h
new file mode 100644
index 000000000..022299e98
--- /dev/null
+++ b/gtk/gtktexttagprivate.h
@@ -0,0 +1,117 @@
+#ifndef GTK_TEXT_TAG_PRIVATE_H
+#define GTK_TEXT_TAG_PRIVATE_H
+
+#include <gtk/gtktexttag.h>
+
+/* values should already have desired defaults; this function will override
+ * the defaults with settings in the given tags, which should be sorted in
+ * ascending order of priority
+*/
+void gtk_text_view_style_values_fill_from_tags (GtkTextStyleValues *values,
+ GtkTextTag **tags,
+ guint n_tags);
+void gtk_text_tag_array_sort (GtkTextTag **tag_array_p,
+ guint len);
+
+/*
+ * Style object created by folding a set of tags together
+ */
+
+typedef struct _GtkTextAppearance GtkTextAppearance;
+
+struct _GtkTextAppearance
+{
+ GdkColor bg_color;
+ GdkColor fg_color;
+ GdkBitmap *bg_stipple;
+ GdkBitmap *fg_stipple;
+
+ guint underline : 4; /* PangoUnderline */
+ guint overstrike : 1;
+
+ /* Whether to use background-related values; this is irrelevant for
+ * the values struct when in a tag, but is used for the composite
+ * values struct; it's true if any of the tags being composited
+ * had background stuff set. */
+ guint draw_bg : 1;
+
+ /* This is only used when we are actually laying out and rendering
+ * a paragraph; not when a GtkTextAppearance is part of a
+ * GtkTextStyleValues.
+ */
+ guint inside_selection : 1;
+};
+
+struct _GtkTextStyleValues
+{
+ guint refcount;
+
+ GtkTextAppearance appearance;
+
+ gint border_width;
+ GtkShadowType relief;
+ GtkJustification justify;
+ GtkTextDirection direction;
+
+ PangoFontDescription *font_desc;
+
+ /* lMargin1 */
+ gint left_margin;
+
+ /* lMargin2 */
+ gint left_wrapped_line_margin;
+
+ /* super/subscript offset, can be negative */
+ gint offset;
+
+ gint right_margin;
+
+ gint pixels_above_lines;
+
+ gint pixels_below_lines;
+
+ gint pixels_inside_wrap;
+
+ GtkTextTabArray *tab_array;
+
+ GtkWrapMode wrap_mode; /* How to handle wrap-around for this tag.
+ * Must be GTK_WRAPMODE_CHAR,
+ * GTK_WRAPMODE_NONE, GTK_WRAPMODE_WORD
+ */
+
+ /* hide the text */
+ guint elide : 1;
+
+ /* Background is fit to full line height rather than
+ * baseline +/- ascent/descent (font height) */
+ guint bg_full_height : 1;
+
+ /* can edit this text */
+ guint editable : 1;
+
+ /* colors are allocated etc. */
+ guint realized : 1;
+
+ guint pad1 : 1;
+ guint pad2 : 1;
+ guint pad3 : 1;
+ guint pad4 : 1;
+};
+
+GtkTextStyleValues *gtk_text_view_style_values_new (void);
+void gtk_text_view_style_values_copy (GtkTextStyleValues *src,
+ GtkTextStyleValues *dest);
+void gtk_text_view_style_values_unref (GtkTextStyleValues *values);
+void gtk_text_view_style_values_ref (GtkTextStyleValues *values);
+
+/* ensure colors are allocated, etc. for drawing */
+void gtk_text_view_style_values_realize (GtkTextStyleValues *values,
+ GdkColormap *cmap,
+ GdkVisual *visual);
+
+/* free the stuff again */
+void gtk_text_view_style_values_unrealize (GtkTextStyleValues *values,
+ GdkColormap *cmap,
+ GdkVisual *visual);
+
+#endif
diff --git a/gtk/gtktexttagtable.c b/gtk/gtktexttagtable.c
new file mode 100644
index 000000000..b204c61b8
--- /dev/null
+++ b/gtk/gtktexttagtable.c
@@ -0,0 +1,252 @@
+
+#include "gtktexttagtable.h"
+#include "gtksignal.h"
+
+#include <stdlib.h>
+
+enum {
+ TAG_CHANGED,
+ TAG_ADDED,
+ TAG_REMOVED,
+ LAST_SIGNAL
+};
+
+enum {
+ LAST_ARG
+};
+
+static void gtk_text_tag_table_init (GtkTextTagTable *table);
+static void gtk_text_tag_table_class_init (GtkTextTagTableClass *klass);
+static void gtk_text_tag_table_destroy (GtkObject *object);
+static void gtk_text_tag_table_finalize (GObject *object);
+static void gtk_text_tag_table_set_arg (GtkObject *object, GtkArg *arg, guint arg_id);
+static void gtk_text_tag_table_get_arg (GtkObject *object, GtkArg *arg, guint arg_id);
+
+static GtkObjectClass *parent_class = NULL;
+static guint signals[LAST_SIGNAL] = { 0 };
+
+GtkType
+gtk_text_tag_table_get_type (void)
+{
+ static GtkType our_type = 0;
+
+ if (our_type == 0)
+ {
+ static const GtkTypeInfo our_info =
+ {
+ "GtkTextTagTable",
+ sizeof (GtkTextTagTable),
+ sizeof (GtkTextTagTableClass),
+ (GtkClassInitFunc) gtk_text_tag_table_class_init,
+ (GtkObjectInitFunc) gtk_text_tag_table_init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL
+ };
+
+ our_type = gtk_type_unique (GTK_TYPE_OBJECT, &our_info);
+ }
+
+ return our_type;
+}
+
+static void
+gtk_text_tag_table_class_init (GtkTextTagTableClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
+
+ parent_class = gtk_type_class (GTK_TYPE_OBJECT);
+
+ signals[TAG_CHANGED] =
+ gtk_signal_new ("tag_changed",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextTagTableClass, tag_changed),
+ gtk_marshal_NONE__POINTER_INT,
+ GTK_TYPE_NONE,
+ 2,
+ GTK_TYPE_OBJECT,
+ GTK_TYPE_BOOL);
+
+ signals[TAG_ADDED] =
+ gtk_signal_new ("tag_added",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextTagTableClass, tag_added),
+ gtk_marshal_NONE__POINTER,
+ GTK_TYPE_NONE,
+ 1,
+ GTK_TYPE_OBJECT);
+
+ signals[TAG_REMOVED] =
+ gtk_signal_new ("tag_removed",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextTagTableClass, tag_removed),
+ gtk_marshal_NONE__POINTER,
+ GTK_TYPE_NONE,
+ 1,
+ GTK_TYPE_OBJECT);
+
+
+ gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+
+ object_class->set_arg = gtk_text_tag_table_set_arg;
+ object_class->get_arg = gtk_text_tag_table_get_arg;
+
+ object_class->destroy = gtk_text_tag_table_destroy;
+ gobject_class->finalize = gtk_text_tag_table_finalize;
+}
+
+void
+gtk_text_tag_table_init (GtkTextTagTable *table)
+{
+ table->hash = g_hash_table_new(g_str_hash, g_str_equal);
+}
+
+GtkTextTagTable*
+gtk_text_tag_table_new (void)
+{
+ GtkTextTagTable *table;
+
+ table = GTK_TEXT_TAG_TABLE (gtk_type_new (gtk_text_tag_table_get_type ()));
+
+ return table;
+}
+
+static void
+gtk_text_tag_table_destroy (GtkObject *object)
+{
+ GtkTextTagTable *table;
+
+ table = GTK_TEXT_TAG_TABLE(object);
+
+ (* GTK_OBJECT_CLASS(parent_class)->destroy) (object);
+}
+
+static void
+gtk_text_tag_table_finalize (GObject *object)
+{
+ GtkTextTagTable *table;
+
+ table = GTK_TEXT_TAG_TABLE(object);
+
+ g_hash_table_destroy(table->hash);
+
+ (* G_OBJECT_CLASS(parent_class)->finalize) (object);
+}
+
+static void
+gtk_text_tag_table_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ GtkTextTagTable *table;
+
+ table = GTK_TEXT_TAG_TABLE(object);
+
+ switch (arg_id)
+ {
+
+ default:
+ g_assert_not_reached();
+ break;
+ }
+}
+
+static void
+gtk_text_tag_table_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ GtkTextTagTable *table;
+
+ table = GTK_TEXT_TAG_TABLE(object);
+
+ switch (arg_id)
+ {
+
+ default:
+ arg->type = GTK_TYPE_INVALID;
+ break;
+ }
+}
+
+void
+gtk_text_tag_table_add(GtkTextTagTable *table, GtkTextTag *tag)
+{
+ guint size;
+
+ g_return_if_fail(GTK_IS_TEXT_TAG_TABLE(table));
+ g_return_if_fail(GTK_IS_OBJECT(tag));
+ g_return_if_fail(g_hash_table_lookup(table->hash, tag->name) == NULL);
+ g_return_if_fail(tag->table == NULL);
+
+ gtk_object_ref(GTK_OBJECT(tag));
+ gtk_object_sink(GTK_OBJECT(tag));
+ g_hash_table_insert(table->hash, tag->name, tag);
+ tag->table = table;
+
+ /* We get the highest tag priority, as the most-recently-added
+ tag. Note that we do NOT use gtk_text_tag_set_priority,
+ as it assumes the tag is already in the table. */
+ size = gtk_text_tag_table_size(table);
+ g_assert(size > 0);
+ tag->priority = size - 1;
+
+ gtk_signal_emit(GTK_OBJECT(table), signals[TAG_ADDED], tag);
+}
+
+GtkTextTag*
+gtk_text_tag_table_lookup(GtkTextTagTable *table, const gchar *name)
+{
+ g_return_val_if_fail(GTK_IS_TEXT_TAG_TABLE(table), NULL);
+ g_return_val_if_fail(name != NULL, NULL);
+
+ return g_hash_table_lookup(table->hash, name);
+}
+
+void
+gtk_text_tag_table_remove(GtkTextTagTable *table, const gchar *name)
+{
+ GtkTextTag *tag;
+
+ g_return_if_fail(GTK_IS_TEXT_TAG_TABLE(table));
+ g_return_if_fail(name != NULL);
+
+ tag = g_hash_table_lookup(table->hash, name);
+
+ if (tag == NULL)
+ return;
+
+ g_return_if_fail(tag->table == table);
+
+ /* Set ourselves to the highest priority; this means
+ when we're removed, there won't be any gaps in the
+ priorities of the tags in the table. */
+ gtk_text_tag_set_priority(tag, gtk_text_tag_table_size(table) - 1);
+
+ tag->table = NULL;
+
+ g_hash_table_remove(table->hash, name);
+
+ gtk_signal_emit(GTK_OBJECT(table), signals[TAG_REMOVED], tag);
+
+ gtk_object_unref(GTK_OBJECT(tag));
+}
+
+void
+gtk_text_tag_table_foreach(GtkTextTagTable *table,
+ GHFunc func,
+ gpointer data)
+{
+ g_return_if_fail(GTK_IS_TEXT_TAG_TABLE(table));
+ g_return_if_fail(func != NULL);
+
+ g_hash_table_foreach(table->hash, func, data);
+}
+
+guint
+gtk_text_tag_table_size(GtkTextTagTable *table)
+{
+ g_return_val_if_fail(GTK_IS_TEXT_TAG_TABLE(table), 0);
+
+ return g_hash_table_size(table->hash);
+}
diff --git a/gtk/gtktexttagtable.h b/gtk/gtktexttagtable.h
new file mode 100644
index 000000000..6624b60ab
--- /dev/null
+++ b/gtk/gtktexttagtable.h
@@ -0,0 +1,53 @@
+#ifndef GTK_TEXT_TAG_TABLE_H
+#define GTK_TEXT_TAG_TABLE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <gtk/gtktexttag.h>
+
+#define GTK_TYPE_TEXT_TAG_TABLE (gtk_text_tag_table_get_type())
+#define GTK_TEXT_TAG_TABLE(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_TEXT_TAG_TABLE, GtkTextTagTable))
+#define GTK_TEXT_TAG_TABLE_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TEXT_TAG_TABLE, GtkTextTagTableClass))
+#define GTK_IS_TEXT_TAG_TABLE(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_TEXT_TAG_TABLE))
+#define GTK_IS_TEXT_TAG_TABLE_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TEXT_TAG_TABLE))
+#define GTK_TEXT_TAG_TABLE_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_TEXT_TAG_TABLE, GtkTextTagTableClass))
+
+typedef struct _GtkTextTagTableClass GtkTextTagTableClass;
+
+struct _GtkTextTagTable {
+ GtkObject parent_instance;
+
+ GHashTable *hash;
+};
+
+struct _GtkTextTagTableClass {
+ GtkObjectClass parent_class;
+
+ void (* tag_changed) (GtkTextTagTable *table, GtkTextTag *tag, gboolean size_changed);
+ void (* tag_added) (GtkTextTagTable *table, GtkTextTag *tag);
+ void (* tag_removed) (GtkTextTagTable *table, GtkTextTag *tag);
+};
+
+GtkType gtk_text_tag_table_get_type (void);
+
+GtkTextTagTable *gtk_text_tag_table_new (void);
+void gtk_text_tag_table_add (GtkTextTagTable *table,
+ GtkTextTag *tag);
+GtkTextTag *gtk_text_tag_table_lookup (GtkTextTagTable *table,
+ const gchar *name);
+void gtk_text_tag_table_remove (GtkTextTagTable *table,
+ const gchar *name);
+void gtk_text_tag_table_foreach (GtkTextTagTable *table,
+ GHFunc func,
+ gpointer data);
+
+guint gtk_text_tag_table_size (GtkTextTagTable *table);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
diff --git a/gtk/gtktexttypes.c b/gtk/gtktexttypes.c
new file mode 100644
index 000000000..cf215af29
--- /dev/null
+++ b/gtk/gtktexttypes.c
@@ -0,0 +1,250 @@
+#include "gtktexttypes.h"
+
+
+/*
+ * Tab array
+ */
+
+GtkTextTabArray*
+gtk_text_view_tab_array_new(guint size)
+{
+ GtkTextTabArray *array;
+
+ array = g_new0(GtkTextTabArray, 1);
+
+ array->refcount = 1;
+ array->numTabs = size;
+ array->tabs = g_new0(GtkTextTab, size);
+
+ return array;
+}
+
+void
+gtk_text_view_tab_array_ref(GtkTextTabArray *tab_array)
+{
+ g_return_if_fail(tab_array != NULL);
+
+ tab_array->refcount += 1;
+}
+
+void
+gtk_text_view_tab_array_unref(GtkTextTabArray *tab_array)
+{
+ g_return_if_fail(tab_array != NULL);
+ g_return_if_fail(tab_array->refcount > 0);
+
+ tab_array->refcount -= 1;
+
+ if (tab_array->refcount == 0)
+ {
+ g_free(tab_array->tabs);
+ g_free(tab_array);
+ }
+}
+
+/*
+ * Unicode stubs (these are wrappers to make libunicode match the Tcl/Tk
+ * API, eventually should just use libunicode/Pango directly)
+ */
+
+#include <unicode.h>
+
+#if 0
+static void
+trigger_efence(const gchar *str, gint len)
+{
+ gchar ch;
+ gint i = 0;
+ while (i < len)
+ {
+ ch = str[i];
+ ((gchar*)str)[i] = ch;
+ ++i;
+ }
+}
+#else
+#define trigger_efence(foo,bar)
+#endif
+
+const GtkTextUniChar gtk_text_unknown_char = 0xFFFD;
+const gchar gtk_text_unknown_char_utf8[] = { 0xEF, 0xBF, 0xBD, '\0' };
+
+gint
+gtk_text_view_num_utf_chars(const gchar *str, gint len)
+{
+ trigger_efence(str, len);
+ return unicode_strlen(str, len);
+}
+
+/* FIXME we need a version of this function with error handling, so we
+ can screen incoming UTF8 for validity. */
+
+gint
+gtk_text_utf_to_unichar(const gchar *str, GtkTextUniChar *chPtr)
+{
+ unicode_char_t ch;
+ gchar *end;
+
+ end = unicode_get_utf8(str, &ch);
+
+ if (end == NULL)
+ g_error("Bad UTF8, need to add some error checking so this doesn't crash the program");
+
+ *chPtr = ch;
+
+ trigger_efence(str, end - str);
+
+ return end - str;
+}
+
+gchar*
+gtk_text_utf_prev(const gchar *str, const gchar *start)
+{
+ gchar *retval;
+
+ trigger_efence(start, str - start);
+
+ retval = unicode_previous_utf8(start, str);
+
+ return retval;
+}
+
+static inline gboolean
+inline_byte_begins_utf8_char(const gchar *byte)
+{
+ return ((*byte & 0xC0) != 0x80);
+}
+
+gboolean
+gtk_text_byte_begins_utf8_char(const gchar *byte)
+{
+ trigger_efence(byte, 1);
+ return inline_byte_begins_utf8_char(byte);
+}
+
+guint
+gtk_text_utf_to_latin1_char(const gchar *p, guchar *l1_ch)
+{
+ guint charlen;
+ GtkTextUniChar ch;
+
+ g_assert(inline_byte_begins_utf8_char(p));
+
+ charlen = gtk_text_utf_to_unichar(p, &ch);
+
+ g_assert(ch != '\0');
+
+ if (ch > 0xff)
+ *l1_ch = '?';
+ else
+ *l1_ch = ch;
+
+ return charlen;
+}
+
+gchar*
+gtk_text_utf_to_latin1(const gchar *p, gint len)
+{
+ GString *str;
+ guint i;
+ gchar *retval;
+
+ trigger_efence(p, len);
+
+ str = g_string_new("");
+
+ i = 0;
+ while (i < len)
+ {
+ guchar ch;
+ guint charlen;
+
+ charlen = gtk_text_utf_to_latin1_char(p+i, &ch);
+
+ g_string_append_c(str, ch);
+
+ i += charlen;
+ }
+
+ retval = str->str;
+ g_string_free(str, FALSE);
+
+ return retval;
+}
+
+static int
+gtk_text_view_unichar_to_utf(GtkTextUniChar c, char *outbuf)
+{
+ size_t len = 0;
+ int first;
+ int i;
+
+ if (c < 0x80)
+ {
+ first = 0;
+ len = 1;
+ }
+ else if (c < 0x800)
+ {
+ first = 0xc0;
+ len = 2;
+ }
+ else if (c < 0x10000)
+ {
+ first = 0xe0;
+ len = 3;
+ }
+ else if (c < 0x200000)
+ {
+ first = 0xf0;
+ len = 4;
+ }
+ else if (c < 0x4000000)
+ {
+ first = 0xf8;
+ len = 5;
+ }
+ else
+ {
+ first = 0xfc;
+ len = 6;
+ }
+
+ for (i = len - 1; i > 0; --i)
+ {
+ outbuf[i] = (c & 0x3f) | 0x80;
+ c >>= 6;
+ }
+ outbuf[0] = c | first;
+
+ return len;
+}
+
+gchar*
+gtk_text_latin1_to_utf (const gchar *latin1, gint len)
+{
+ gint i;
+ GString *retval;
+ gchar *str;
+
+ retval = g_string_new("");
+
+ i = 0;
+ while (i < len)
+ {
+ gchar utf[7];
+ gint count;
+
+ count = gtk_text_view_unichar_to_utf((guchar)latin1[i], utf);
+
+ utf[count] = '\0';
+
+ g_string_append(retval, utf);
+
+ ++i;
+ }
+
+ str = retval->str;
+ g_string_free(retval, FALSE);
+ return str;
+}
diff --git a/gtk/gtktexttypes.h b/gtk/gtktexttypes.h
new file mode 100644
index 000000000..d8c284a38
--- /dev/null
+++ b/gtk/gtktexttypes.h
@@ -0,0 +1,150 @@
+#ifndef GTK_TEXT_TYPES_H
+#define GTK_TEXT_TYPES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <glib.h>
+
+#include <gtk/gtktextbuffer.h>
+
+typedef unsigned short GtkTextUniChar; /* Unicode character */
+
+#include <gtk/gtktexttagprivate.h>
+
+typedef struct _GtkTextLine GtkTextLine;
+typedef struct _GtkTextCounter GtkTextCounter;
+typedef struct _GtkTextLineSegment GtkTextLineSegment;
+typedef struct _GtkTextLineSegmentClass GtkTextLineSegmentClass;
+typedef struct _GtkTextToggleBody GtkTextToggleBody;
+typedef struct _GtkTextViewSearch GtkTextViewSearch;
+typedef struct _GtkTextTab GtkTextTab;
+typedef struct _GtkTextViewStyle GtkTextViewStyle;
+typedef struct _GtkTextMarkBody GtkTextMarkBody;
+typedef struct _GtkTextLayout GtkTextLayout;
+
+/*
+ * Search
+ */
+
+/*
+ * The data structure below is used for searching a B-tree for transitions
+ * on a single tag (or for all tag transitions). No code outside of
+ * tkTextBTree.c should ever modify any of the fields in these structures,
+ * but it's OK to use them for read-only information.
+ */
+
+struct _GtkTextViewSearch {
+ GtkTextBTree *tree;
+
+ GtkTextIter curIndex; /* Position of last tag transition
+ * returned by gtk_text_btree_next_tag, or
+ * index of start of segment
+ * containing starting position for
+ * search if gtk_text_btree_next_tag hasn't
+ * been called yet, or same as
+ * stopIndex if search is over. */
+
+ GtkTextLineSegment *segPtr; /* Actual tag segment returned
+ by last call to
+ gtk_text_btree_next_tag,
+ or NULL if
+ gtk_text_btree_next_tag
+ hasn't returned anything
+ yet. */
+
+ GtkTextLineSegment *lastPtr; /* Stop search before just before
+ * considering this segment. */
+ GtkTextTag *tag; /* Tag to search for (or tag found, if
+ * allTags is non-zero). */
+ int linesLeft; /* Lines left to search (including
+ * curIndex and stopIndex). When
+ * this becomes <= 0 the search is
+ * over. */
+ int allTags; /* Non-zero means ignore tag check:
+ * search for transitions on all
+ * tags. */
+};
+
+/*
+ * The following data structure describes a single tab stop.
+ */
+
+typedef enum {
+ GTK_TEXT_TAB_LEFT,
+ GTK_TEXT_TAB_RIGHT,
+ GTK_TEXT_TAB_CENTER,
+ GTK_TEXT_TAB_NUMERIC
+} GtkTextTabAlign;
+
+struct _GtkTextTab {
+ int location; /* Offset in pixels of this tab stop
+ * from the left margin (lmargin2) of
+ * the text. */
+ GtkTextTabAlign alignment; /* Where the tab stop appears relative
+ * to the text. */
+};
+
+struct _GtkTextTabArray {
+ guint refcount;
+ int numTabs; /* Number of tab stops. */
+ GtkTextTab *tabs;
+};
+
+GtkTextTabArray *gtk_text_view_tab_array_new (guint size);
+void gtk_text_view_tab_array_ref (GtkTextTabArray *tab_array);
+void gtk_text_view_tab_array_unref (GtkTextTabArray *tab_array);
+
+/*
+ * The constant below is used to specify a line when what is really
+ * wanted is the entire text. For now, just use a very big number.
+ */
+
+#define GTK_TEXT_END_OF_TEXT 1000000
+
+/*
+ * The following definition specifies the maximum number of characters
+ * needed in a string to hold a position specifier.
+ */
+
+#define GTK_TEXT_POS_CHARS 30
+
+/*
+ * Declarations for variables shared among the text-related files:
+ */
+
+/* In gtktextbtree.c */
+extern GtkTextLineSegmentClass gtk_text_char_type;
+extern GtkTextLineSegmentClass gtk_text_toggle_on_type;
+extern GtkTextLineSegmentClass gtk_text_toggle_off_type;
+
+/* In gtktextmark.c */
+extern GtkTextLineSegmentClass gtk_text_left_mark_type;
+extern GtkTextLineSegmentClass gtk_text_right_mark_type;
+
+/* In gtktextchild.c */
+extern GtkTextLineSegmentClass gtk_text_pixmap_type;
+extern GtkTextLineSegmentClass gtk_text_view_child_type;
+
+/*
+ * UTF 8 Stubs
+ */
+
+extern const GtkTextUniChar gtk_text_unknown_char;
+extern const gchar gtk_text_unknown_char_utf8[];
+
+gint gtk_text_view_num_utf_chars(const gchar *chars, gint len);
+gint gtk_text_utf_to_unichar(const gchar *p, GtkTextUniChar *ch);
+gchar *gtk_text_utf_prev(const gchar *p, const gchar *p2);
+gchar *gtk_text_utf_to_latin1(const gchar *p, gint len);
+gboolean gtk_text_byte_begins_utf8_char(const gchar *byte);
+guint gtk_text_utf_to_latin1_char(const gchar *p, guchar *ch);
+gchar *gtk_text_latin1_to_utf (const gchar *latin1, gint len);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif
+
diff --git a/gtk/gtktextview.c b/gtk/gtktextview.c
new file mode 100644
index 000000000..31bc165b2
--- /dev/null
+++ b/gtk/gtktextview.c
@@ -0,0 +1,2899 @@
+/* gtktext.c - A "view" widget for the GtkTextBuffer object
+ *
+ * Copyright (c) 1992-1994 The Regents of the University of California.
+ * Copyright (c) 1994-1996 Sun Microsystems, Inc.
+ * Copyright (c) 1999 by Scriptics Corporation.
+ * Copyright (c) 2000 Red Hat, Inc.
+ * Tk -> Gtk port by Havoc Pennington <hp@redhat.com>
+ *
+ * This software is copyrighted by the Regents of the University of
+ * California, Sun Microsystems, Inc., and other parties. The
+ * following terms apply to all files associated with the software
+ * unless explicitly disclaimed in individual files.
+ *
+ * The authors hereby grant permission to use, copy, modify,
+ * distribute, and license this software and its documentation for any
+ * purpose, provided that existing copyright notices are retained in
+ * all copies and that this notice is included verbatim in any
+ * distributions. No written agreement, license, or royalty fee is
+ * required for any of the authorized uses. Modifications to this
+ * software may be copyrighted by their authors and need not follow
+ * the licensing terms described here, provided that the new terms are
+ * clearly indicated on the first page of each file where they apply.
+ *
+ * IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
+ * DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION,
+ * OR ANY DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
+ * NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS,
+ * AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
+ * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * GOVERNMENT USE: If you are acquiring this software on behalf of the
+ * U.S. government, the Government shall have only "Restricted Rights"
+ * in the software and related documentation as defined in the Federal
+ * Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
+ * are acquiring the software on behalf of the Department of Defense,
+ * the software shall be classified as "Commercial Computer Software"
+ * and the Government shall have only "Restricted Rights" as defined
+ * in Clause 252.227-7013 (c) (1) of DFARs. Notwithstanding the
+ * foregoing, the authors grant the U.S. Government and others acting
+ * in its behalf permission to use and distribute the software in
+ * accordance with the terms specified in this license.
+ *
+ */
+
+#include "gtkbindings.h"
+#include "gtkdnd.h"
+#include "gtkmain.h"
+#include "gtksignal.h"
+#include "gtktext.h"
+#include "gtktextdisplay.h"
+#include "gtktextview.h"
+#include "gtkimmulticontext.h"
+#include "gdk/gdkkeysyms.h"
+
+enum {
+ MOVE_INSERT,
+ SET_ANCHOR,
+ SCROLL_TEXT,
+ DELETE_TEXT,
+ CUT_TEXT,
+ COPY_TEXT,
+ PASTE_TEXT,
+ TOGGLE_OVERWRITE,
+ SET_SCROLL_ADJUSTMENTS,
+ LAST_SIGNAL
+};
+
+enum {
+ ARG_0,
+ ARG_HEIGHT_LINES,
+ ARG_WIDTH_COLUMNS,
+ ARG_PIXELS_ABOVE_LINES,
+ ARG_PIXELS_BELOW_LINES,
+ ARG_PIXELS_INSIDE_WRAP,
+ ARG_EDITABLE,
+ ARG_WRAP_MODE,
+ LAST_ARG
+};
+
+static void gtk_text_view_init (GtkTextView *text_view);
+static void gtk_text_view_class_init (GtkTextViewClass *klass);
+static void gtk_text_view_destroy (GtkObject *object);
+static void gtk_text_view_finalize (GObject *object);
+static void gtk_text_view_set_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id);
+static void gtk_text_view_get_arg (GtkObject *object,
+ GtkArg *arg,
+ guint arg_id);
+static void gtk_text_view_size_request (GtkWidget *widget,
+ GtkRequisition *requisition);
+static void gtk_text_view_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation);
+static void gtk_text_view_realize (GtkWidget *widget);
+static void gtk_text_view_unrealize (GtkWidget *widget);
+static void gtk_text_view_style_set (GtkWidget *widget,
+ GtkStyle *previous_style);
+static gint gtk_text_view_event (GtkWidget *widget,
+ GdkEvent *event);
+static gint gtk_text_view_key_press_event (GtkWidget *widget,
+ GdkEventKey *event);
+static gint gtk_text_view_key_release_event (GtkWidget *widget,
+ GdkEventKey *event);
+static gint gtk_text_view_button_press_event (GtkWidget *widget,
+ GdkEventButton *event);
+static gint gtk_text_view_button_release_event (GtkWidget *widget,
+ GdkEventButton *event);
+static gint gtk_text_view_focus_in_event (GtkWidget *widget,
+ GdkEventFocus *event);
+static gint gtk_text_view_focus_out_event (GtkWidget *widget,
+ GdkEventFocus *event);
+static gint gtk_text_view_motion_event (GtkWidget *widget,
+ GdkEventMotion *event);
+static void gtk_text_view_draw (GtkWidget *widget,
+ GdkRectangle *area);
+static gint gtk_text_view_expose_event (GtkWidget *widget,
+ GdkEventExpose *expose);
+
+
+/* Source side drag signals */
+static void gtk_text_view_drag_begin (GtkWidget *widget,
+ GdkDragContext *context);
+static void gtk_text_view_drag_end (GtkWidget *widget,
+ GdkDragContext *context);
+static void gtk_text_view_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time);
+static void gtk_text_view_drag_data_delete (GtkWidget *widget,
+ GdkDragContext *context);
+
+/* Target side drag signals */
+static void gtk_text_view_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time);
+static gboolean gtk_text_view_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time);
+static gboolean gtk_text_view_drag_drop (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time);
+static void gtk_text_view_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time);
+
+static void gtk_text_view_set_scroll_adjustments (GtkTextView *text_view,
+ GtkAdjustment *hadj,
+ GtkAdjustment *vadj);
+
+static void gtk_text_view_move_insert (GtkTextView *text_view,
+ GtkTextViewMovementStep step,
+ gint count,
+ gboolean extend_selection);
+static void gtk_text_view_set_anchor (GtkTextView *text_view);
+static void gtk_text_view_scroll_text (GtkTextView *text_view,
+ GtkTextViewScrollType type);
+static void gtk_text_view_delete_text (GtkTextView *text_view,
+ GtkTextViewDeleteType type,
+ gint count);
+static void gtk_text_view_cut_text (GtkTextView *text_view);
+static void gtk_text_view_copy_text (GtkTextView *text_view);
+static void gtk_text_view_paste_text (GtkTextView *text_view);
+static void gtk_text_view_toggle_overwrite (GtkTextView *text_view);
+
+
+static void gtk_text_view_validate_onscreen (GtkTextView *text_view);
+static void gtk_text_view_get_first_para_iter (GtkTextView *text_view,
+ GtkTextIter *iter);
+static void gtk_text_view_scroll_calc_now (GtkTextView *text_view);
+static void gtk_text_view_set_values_from_style (GtkTextView *text_view,
+ GtkTextStyleValues *values,
+ GtkStyle *style);
+static void gtk_text_view_ensure_layout (GtkTextView *text_view);
+static void gtk_text_view_destroy_layout (GtkTextView *text_view);
+static void gtk_text_view_start_selection_drag (GtkTextView *text_view,
+ const GtkTextIter *iter,
+ GdkEventButton *event);
+static gboolean gtk_text_view_end_selection_drag (GtkTextView *text_view,
+ GdkEventButton *event);
+static void gtk_text_view_start_selection_dnd (GtkTextView *text_view,
+ const GtkTextIter *iter,
+ GdkEventButton *event);
+static void gtk_text_view_start_cursor_blink (GtkTextView *text_view);
+static void gtk_text_view_stop_cursor_blink (GtkTextView *text_view);
+
+static void gtk_text_view_value_changed (GtkAdjustment *adj,
+ GtkTextView *view);
+static void gtk_text_view_commit_handler (GtkIMContext *context,
+ const gchar *str,
+ GtkTextView *text_view);
+
+static void gtk_text_view_mark_set_handler (GtkTextBuffer *buffer,
+ const GtkTextIter *location,
+ const char *mark_name,
+ gpointer data);
+static void gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view,
+ gint *x,
+ gint *y);
+static void gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view,
+ gint x,
+ gint y);
+
+enum {
+ TARGET_STRING,
+ TARGET_TEXT,
+ TARGET_COMPOUND_TEXT,
+ TARGET_UTF8_STRING
+};
+
+static GdkAtom clipboard_atom = GDK_NONE;
+static GdkAtom text_atom = GDK_NONE;
+static GdkAtom ctext_atom = GDK_NONE;
+static GdkAtom utf8_atom = GDK_NONE;
+
+
+static GtkTargetEntry target_table[] = {
+ { "UTF8_STRING", 0, TARGET_UTF8_STRING },
+ { "COMPOUND_TEXT", 0, TARGET_COMPOUND_TEXT },
+ { "TEXT", 0, TARGET_TEXT },
+ { "text/plain", 0, TARGET_STRING },
+ { "STRING", 0, TARGET_STRING }
+};
+
+static guint n_targets = sizeof (target_table) / sizeof (target_table[0]);
+
+static GtkContainerClass *parent_class = NULL;
+static guint signals[LAST_SIGNAL] = { 0 };
+
+GtkType
+gtk_text_view_get_type (void)
+{
+ static GtkType our_type = 0;
+
+ if (our_type == 0)
+ {
+ static const GtkTypeInfo our_info =
+ {
+ "GtkTextView",
+ sizeof (GtkTextView),
+ sizeof (GtkTextViewClass),
+ (GtkClassInitFunc) gtk_text_view_class_init,
+ (GtkObjectInitFunc) gtk_text_view_init,
+ /* reserved_1 */ NULL,
+ /* reserved_2 */ NULL,
+ (GtkClassInitFunc) NULL
+ };
+
+ our_type = gtk_type_unique (GTK_TYPE_CONTAINER, &our_info);
+ }
+
+ return our_type;
+}
+
+static void
+add_move_insert_binding (GtkBindingSet *binding_set,
+ guint keyval,
+ guint modmask,
+ GtkTextViewMovementStep step,
+ gint count)
+{
+ g_return_if_fail ((modmask & GDK_SHIFT_MASK) == 0);
+
+ gtk_binding_entry_add_signal (binding_set, keyval, modmask,
+ "move_insert", 3,
+ GTK_TYPE_ENUM, step,
+ GTK_TYPE_INT, count,
+ GTK_TYPE_BOOL, FALSE);
+
+ /* Selection-extending version */
+ gtk_binding_entry_add_signal (binding_set, keyval, modmask | GDK_SHIFT_MASK,
+ "move_insert", 3,
+ GTK_TYPE_ENUM, step,
+ GTK_TYPE_INT, count,
+ GTK_TYPE_BOOL, TRUE);
+}
+
+static void
+gtk_text_view_class_init (GtkTextViewClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GtkObjectClass *object_class = GTK_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+ GtkBindingSet *binding_set;
+
+ parent_class = gtk_type_class (GTK_TYPE_CONTAINER);
+
+ /*
+ * Arguments
+ */
+ gtk_object_add_arg_type ("GtkTextView::height_lines", GTK_TYPE_INT,
+ GTK_ARG_READWRITE, ARG_HEIGHT_LINES);
+ gtk_object_add_arg_type ("GtkTextView::width_columns", GTK_TYPE_INT,
+ GTK_ARG_READWRITE, ARG_WIDTH_COLUMNS);
+ gtk_object_add_arg_type ("GtkTextView::pixels_above_lines", GTK_TYPE_INT,
+ GTK_ARG_READWRITE, ARG_PIXELS_ABOVE_LINES);
+ gtk_object_add_arg_type ("GtkTextView::pixels_below_lines", GTK_TYPE_INT,
+ GTK_ARG_READWRITE, ARG_PIXELS_BELOW_LINES);
+ gtk_object_add_arg_type ("GtkTextView::pixels_inside_wrap", GTK_TYPE_INT,
+ GTK_ARG_READWRITE, ARG_PIXELS_INSIDE_WRAP);
+ gtk_object_add_arg_type ("GtkTextView::editable", GTK_TYPE_BOOL,
+ GTK_ARG_READWRITE, ARG_EDITABLE);
+ gtk_object_add_arg_type ("GtkTextView::wrap_mode", GTK_TYPE_ENUM,
+ GTK_ARG_READWRITE, ARG_WRAP_MODE);
+
+
+ /*
+ * Signals
+ */
+
+ signals[MOVE_INSERT] =
+ gtk_signal_new ("move_insert",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextViewClass, move_insert),
+ gtk_marshal_NONE__INT_INT_INT,
+ GTK_TYPE_NONE, 3, GTK_TYPE_ENUM, GTK_TYPE_INT, GTK_TYPE_BOOL);
+
+ signals[SET_ANCHOR] =
+ gtk_signal_new ("set_anchor",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextViewClass, set_anchor),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ signals[SCROLL_TEXT] =
+ gtk_signal_new ("scroll_text",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextViewClass, scroll_text),
+ gtk_marshal_NONE__INT,
+ GTK_TYPE_NONE, 1, GTK_TYPE_ENUM);
+
+ signals[DELETE_TEXT] =
+ gtk_signal_new ("delete_text",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextViewClass, delete_text),
+ gtk_marshal_NONE__INT_INT,
+ GTK_TYPE_NONE, 2, GTK_TYPE_ENUM, GTK_TYPE_INT);
+
+ signals[CUT_TEXT] =
+ gtk_signal_new ("cut_text",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextViewClass, cut_text),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ signals[COPY_TEXT] =
+ gtk_signal_new ("copy_text",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextViewClass, copy_text),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ signals[PASTE_TEXT] =
+ gtk_signal_new ("paste_text",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextViewClass, paste_text),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ signals[TOGGLE_OVERWRITE] =
+ gtk_signal_new ("toggle_overwrite",
+ GTK_RUN_LAST | GTK_RUN_ACTION,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextViewClass, toggle_overwrite),
+ gtk_marshal_NONE__NONE,
+ GTK_TYPE_NONE, 0);
+
+ signals[SET_SCROLL_ADJUSTMENTS] = widget_class->set_scroll_adjustments_signal =
+ gtk_signal_new ("set_scroll_adjustments",
+ GTK_RUN_LAST,
+ GTK_CLASS_TYPE (object_class),
+ GTK_SIGNAL_OFFSET (GtkTextViewClass, set_scroll_adjustments),
+ gtk_marshal_NONE__POINTER_POINTER,
+ GTK_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
+
+ gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
+
+ /*
+ * Key bindings
+ */
+
+ binding_set = gtk_binding_set_by_class (klass);
+
+ /* Moving the insertion point */
+ add_move_insert_binding (binding_set, GDK_Right, 0,
+ GTK_TEXT_MOVEMENT_POSITIONS, 1);
+
+ add_move_insert_binding (binding_set, GDK_Left, 0,
+ GTK_TEXT_MOVEMENT_POSITIONS, -1);
+
+ add_move_insert_binding (binding_set, GDK_f, GDK_CONTROL_MASK,
+ GTK_TEXT_MOVEMENT_CHAR, 1);
+
+ add_move_insert_binding (binding_set, GDK_b, GDK_CONTROL_MASK,
+ GTK_TEXT_MOVEMENT_CHAR, -1);
+
+ add_move_insert_binding (binding_set, GDK_Right, GDK_CONTROL_MASK,
+ GTK_TEXT_MOVEMENT_WORD, 1);
+
+ add_move_insert_binding (binding_set, GDK_Left, GDK_CONTROL_MASK,
+ GTK_TEXT_MOVEMENT_WORD, -1);
+
+ /* Eventually we want to move by display lines, not paragraphs */
+ add_move_insert_binding (binding_set, GDK_Up, 0,
+ GTK_TEXT_MOVEMENT_LINE, -1);
+
+ add_move_insert_binding (binding_set, GDK_Down, 0,
+ GTK_TEXT_MOVEMENT_LINE, 1);
+
+ add_move_insert_binding (binding_set, GDK_p, GDK_CONTROL_MASK,
+ GTK_TEXT_MOVEMENT_LINE, -1);
+
+ add_move_insert_binding (binding_set, GDK_n, GDK_CONTROL_MASK,
+ GTK_TEXT_MOVEMENT_LINE, 1);
+
+ add_move_insert_binding (binding_set, GDK_a, GDK_CONTROL_MASK,
+ GTK_TEXT_MOVEMENT_PARAGRAPH_ENDS, -1);
+
+ add_move_insert_binding (binding_set, GDK_e, GDK_CONTROL_MASK,
+ GTK_TEXT_MOVEMENT_PARAGRAPH_ENDS, 1);
+
+ add_move_insert_binding (binding_set, GDK_f, GDK_MOD1_MASK,
+ GTK_TEXT_MOVEMENT_WORD, 1);
+
+ add_move_insert_binding (binding_set, GDK_b, GDK_MOD1_MASK,
+ GTK_TEXT_MOVEMENT_WORD, -1);
+
+ add_move_insert_binding (binding_set, GDK_Home, 0,
+ GTK_TEXT_MOVEMENT_BUFFER_ENDS, -1);
+
+ add_move_insert_binding (binding_set, GDK_End, 0,
+ GTK_TEXT_MOVEMENT_BUFFER_ENDS, 1);
+
+ /* Setting the cut/paste/copy anchor */
+ gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_CONTROL_MASK,
+ "set_anchor", 0);
+
+
+ /* Scrolling around */
+ gtk_binding_entry_add_signal (binding_set, GDK_Page_Up, 0,
+ "scroll_text", 1,
+ GTK_TYPE_ENUM, GTK_TEXT_SCROLL_PAGE_UP);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_Page_Down, 0,
+ "scroll_text", 1,
+ GTK_TYPE_ENUM, GTK_TEXT_SCROLL_PAGE_DOWN);
+
+ /* Deleting text */
+ gtk_binding_entry_add_signal (binding_set, GDK_Delete, 0,
+ "delete_text", 2,
+ GTK_TYPE_ENUM, GTK_TEXT_DELETE_CHAR,
+ GTK_TYPE_INT, 1);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, 0,
+ "delete_text", 2,
+ GTK_TYPE_ENUM, GTK_TEXT_DELETE_CHAR,
+ GTK_TYPE_INT, -1);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_Delete, GDK_CONTROL_MASK,
+ "delete_text", 2,
+ GTK_TYPE_ENUM, GTK_TEXT_DELETE_HALF_WORD,
+ GTK_TYPE_INT, 1);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_BackSpace, GDK_CONTROL_MASK,
+ "delete_text", 2,
+ GTK_TYPE_ENUM, GTK_TEXT_DELETE_HALF_WORD,
+ GTK_TYPE_INT, -1);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_k, GDK_CONTROL_MASK,
+ "delete_text", 2,
+ GTK_TYPE_ENUM, GTK_TEXT_DELETE_HALF_PARAGRAPH,
+ GTK_TYPE_INT, 1);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_u, GDK_CONTROL_MASK,
+ "delete_text", 2,
+ GTK_TYPE_ENUM, GTK_TEXT_DELETE_WHOLE_PARAGRAPH,
+ GTK_TYPE_INT, 1);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_space, GDK_MOD1_MASK,
+ "delete_text", 2,
+ GTK_TYPE_ENUM, GTK_TEXT_DELETE_WHITESPACE_LEAVE_ONE,
+ GTK_TYPE_INT, 1);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_backslash, GDK_MOD1_MASK,
+ "delete_text", 2,
+ GTK_TYPE_ENUM, GTK_TEXT_DELETE_WHITESPACE,
+ GTK_TYPE_INT, 1);
+
+ /* Cut/copy/paste */
+
+ gtk_binding_entry_add_signal (binding_set, GDK_x, GDK_CONTROL_MASK,
+ "cut_text", 0);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_w, GDK_CONTROL_MASK,
+ "cut_text", 0);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_c, GDK_CONTROL_MASK,
+ "copy_text", 0);
+
+ gtk_binding_entry_add_signal (binding_set, GDK_y, GDK_CONTROL_MASK,
+ "paste_text", 0);
+
+ /* Overwrite */
+ gtk_binding_entry_add_signal (binding_set, GDK_Insert, 0,
+ "toggle_overwrite", 0);
+
+ /*
+ * Default handlers and virtual methods
+ */
+ object_class->set_arg = gtk_text_view_set_arg;
+ object_class->get_arg = gtk_text_view_get_arg;
+
+ object_class->destroy = gtk_text_view_destroy;
+ gobject_class->finalize = gtk_text_view_finalize;
+
+ widget_class->realize = gtk_text_view_realize;
+ widget_class->unrealize = gtk_text_view_unrealize;
+ widget_class->style_set = gtk_text_view_style_set;
+ widget_class->size_request = gtk_text_view_size_request;
+ widget_class->size_allocate = gtk_text_view_size_allocate;
+ widget_class->event = gtk_text_view_event;
+ widget_class->key_press_event = gtk_text_view_key_press_event;
+ widget_class->key_release_event = gtk_text_view_key_release_event;
+ widget_class->button_press_event = gtk_text_view_button_press_event;
+ widget_class->button_release_event = gtk_text_view_button_release_event;
+ widget_class->focus_in_event = gtk_text_view_focus_in_event;
+ widget_class->focus_out_event = gtk_text_view_focus_out_event;
+ widget_class->motion_notify_event = gtk_text_view_motion_event;
+ widget_class->expose_event = gtk_text_view_expose_event;
+ widget_class->draw = gtk_text_view_draw;
+
+ widget_class->drag_begin = gtk_text_view_drag_begin;
+ widget_class->drag_end = gtk_text_view_drag_end;
+ widget_class->drag_data_get = gtk_text_view_drag_data_get;
+ widget_class->drag_data_delete = gtk_text_view_drag_data_delete;
+
+ widget_class->drag_leave = gtk_text_view_drag_leave;
+ widget_class->drag_motion = gtk_text_view_drag_motion;
+ widget_class->drag_drop = gtk_text_view_drag_drop;
+ widget_class->drag_data_received = gtk_text_view_drag_data_received;
+
+ klass->move_insert = gtk_text_view_move_insert;
+ klass->set_anchor = gtk_text_view_set_anchor;
+ klass->scroll_text = gtk_text_view_scroll_text;
+ klass->delete_text = gtk_text_view_delete_text;
+ klass->cut_text = gtk_text_view_cut_text;
+ klass->copy_text = gtk_text_view_copy_text;
+ klass->paste_text = gtk_text_view_paste_text;
+ klass->toggle_overwrite = gtk_text_view_toggle_overwrite;
+ klass->set_scroll_adjustments = gtk_text_view_set_scroll_adjustments;
+}
+
+void
+gtk_text_view_init (GtkTextView *text_view)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (text_view);
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
+
+ text_view->wrap_mode = GTK_WRAPMODE_NONE;
+
+ if (!clipboard_atom)
+ clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE);
+
+ if (!text_atom)
+ text_atom = gdk_atom_intern ("TEXT", FALSE);
+
+ if (!ctext_atom)
+ ctext_atom = gdk_atom_intern ("COMPOUND_TEXT", FALSE);
+
+ if (!utf8_atom)
+ utf8_atom = gdk_atom_intern ("UTF8_STRING", FALSE);
+
+ gtk_drag_dest_set (widget,
+ GTK_DEST_DEFAULT_DROP | GTK_DEST_DEFAULT_MOTION,
+ target_table, n_targets,
+ GDK_ACTION_COPY | GDK_ACTION_MOVE);
+
+ text_view->virtual_cursor_x = -1;
+ text_view->virtual_cursor_y = -1;
+
+ /* This object is completely private. No external entity can gain a reference
+ * to it; so we create it here and destroy it in finalize().
+ */
+ text_view->im_context = gtk_im_multicontext_new ();
+
+ gtk_signal_connect (GTK_OBJECT (text_view->im_context), "commit",
+ GTK_SIGNAL_FUNC (gtk_text_view_commit_handler), text_view);
+}
+
+GtkWidget*
+gtk_text_view_new (void)
+{
+ return GTK_WIDGET (gtk_type_new (gtk_text_view_get_type ()));
+}
+
+GtkWidget*
+gtk_text_view_new_with_buffer (GtkTextBuffer *buffer)
+{
+ GtkTextView *text_view;
+
+ text_view = (GtkTextView*)gtk_text_view_new ();
+
+ gtk_text_view_set_buffer (text_view, buffer);
+
+ return GTK_WIDGET (text_view);
+}
+
+void
+gtk_text_view_set_buffer (GtkTextView *text_view,
+ GtkTextBuffer *buffer)
+{
+ g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
+ g_return_if_fail (buffer == NULL || GTK_IS_TEXT_BUFFER (buffer));
+
+ if (text_view->buffer == buffer)
+ return;
+
+ if (text_view->buffer != NULL)
+ {
+ gtk_signal_disconnect_by_func (GTK_OBJECT (text_view->buffer),
+ gtk_text_view_mark_set_handler, text_view);
+ gtk_object_unref (GTK_OBJECT (text_view->buffer));
+ text_view->dnd_mark = NULL;
+ }
+
+ text_view->buffer = buffer;
+
+ if (buffer != NULL)
+ {
+ char *mark_name;
+
+ GtkTextIter start;
+
+ gtk_object_ref (GTK_OBJECT (buffer));
+ gtk_object_sink (GTK_OBJECT (buffer));
+
+ if (text_view->layout)
+ gtk_text_layout_set_buffer (text_view->layout, buffer);
+
+ gtk_text_buffer_get_iter_at_char (text_view->buffer, &start, 0);
+
+ text_view->dnd_mark = gtk_text_buffer_create_mark (text_view->buffer,
+ "__drag_target",
+ &start, FALSE);
+
+ /* Initialize. FIXME: Allow anonymous marks and use one here
+ */
+ mark_name = g_strdup_printf ("__first_para_%p", text_view);
+ text_view->first_para_mark = gtk_text_buffer_create_mark (text_view->buffer,
+ mark_name,
+ &start, TRUE);
+ g_free (mark_name);
+ text_view->first_para_pixels = 0;
+
+ gtk_signal_connect (GTK_OBJECT (text_view->buffer), "mark_set",
+ gtk_text_view_mark_set_handler, text_view);
+ }
+
+ if (GTK_WIDGET_VISIBLE (text_view))
+ gtk_widget_queue_draw (GTK_WIDGET (text_view));
+}
+
+GtkTextBuffer*
+gtk_text_view_get_buffer (GtkTextView *text_view)
+{
+ g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), NULL);
+
+ return text_view->buffer;
+}
+
+void
+gtk_text_view_get_iter_at_pixel (GtkTextView *text_view,
+ GtkTextIter *iter,
+ gint x, gint y)
+{
+ g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
+ g_return_if_fail (iter != NULL);
+ g_return_if_fail (text_view->layout != NULL);
+
+ gtk_text_layout_get_iter_at_pixel (text_view->layout,
+ iter,
+ x + text_view->xoffset,
+ y + text_view->yoffset);
+}
+
+
+static void
+set_adjustment_clamped (GtkAdjustment *adj, gfloat val)
+{
+ /* We don't really want to clamp to upper; we want to clamp to
+ upper - page_size which is the highest value the scrollbar
+ will let us reach. */
+ if (val > (adj->upper - adj->page_size))
+ val = adj->upper - adj->page_size;
+
+ if (val < adj->lower)
+ val = adj->lower;
+
+ gtk_adjustment_set_value (adj, val);
+}
+
+gboolean
+gtk_text_view_scroll_to_mark_adjusted (GtkTextView *text_view,
+ const gchar *mark_name,
+ gint margin,
+ gfloat percentage)
+{
+ GtkTextIter iter;
+ GdkRectangle rect;
+ GdkRectangle screen;
+ gint screen_bottom;
+ gint screen_right;
+ GtkWidget *widget;
+ gboolean retval = FALSE;
+ gint scroll_inc;
+
+ gint current_x_scroll, current_y_scroll;
+
+ g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
+ g_return_val_if_fail (mark_name != NULL, FALSE);
+
+ widget = GTK_WIDGET (text_view);
+
+ if (!GTK_WIDGET_MAPPED (widget))
+ {
+ g_warning ("FIXME need to implement scroll_to_mark for unmapped GtkTextView?");
+ return FALSE;
+ }
+
+ if (!gtk_text_buffer_get_iter_at_mark (text_view->buffer, &iter, mark_name))
+ {
+ g_warning ("Mark %s does not exist! can't scroll to it.", mark_name);
+ return FALSE;
+ }
+
+ gtk_text_layout_get_iter_location (text_view->layout,
+ &iter,
+ &rect);
+
+ /* Be sure the scroll region is up-to-date */
+ gtk_text_view_scroll_calc_now (text_view);
+
+ current_x_scroll = text_view->xoffset;
+ current_y_scroll = text_view->yoffset;
+
+ screen.x = current_x_scroll;
+ screen.y = current_y_scroll;
+ screen.width = widget->allocation.width;
+ screen.height = widget->allocation.height;
+
+ {
+ /* Clamp margin so it's not too large. */
+ gint small_dimension = MIN (screen.width, screen.height);
+ gint max_rect_dim;
+
+ if (margin > (small_dimension/2 - 5)) /* 5 is arbitrary */
+ margin = (small_dimension/2 - 5);
+
+ if (margin < 0)
+ margin = 0;
+
+ /* make sure rectangle fits in the leftover space */
+
+ max_rect_dim = MAX (rect.width, rect.height);
+
+ if (max_rect_dim > (small_dimension - margin*2))
+ margin -= max_rect_dim - (small_dimension - margin*2);
+
+ if (margin < 0)
+ margin = 0;
+ }
+
+ g_assert (margin >= 0);
+
+ screen.x += margin;
+ screen.y += margin;
+
+ screen.width -= margin*2;
+ screen.height -= margin*2;
+
+ screen_bottom = screen.y + screen.height;
+ screen_right = screen.x + screen.width;
+
+ /* Vertical scroll (only vertical gets adjusted) */
+
+ scroll_inc = 0;
+ if (rect.y < screen.y)
+ {
+ gint scroll_dest = rect.y;
+ scroll_inc = (scroll_dest - screen.y) * percentage;
+ }
+ else if ((rect.y + rect.height) > screen_bottom)
+ {
+ gint scroll_dest = rect.y + rect.height;
+ scroll_inc = (scroll_dest - screen_bottom) * percentage;
+ }
+
+ if (scroll_inc != 0)
+ {
+ set_adjustment_clamped (text_view->vadjustment,
+ current_y_scroll + scroll_inc);
+ retval = TRUE;
+ }
+
+ /* Horizontal scroll */
+
+ scroll_inc = 0;
+ if (rect.x < screen.x)
+ {
+ gint scroll_dest = rect.x;
+ scroll_inc = scroll_dest - screen.x;
+ }
+ else if ((rect.x + rect.width) > screen_right)
+ {
+ gint scroll_dest = rect.x + rect.width;
+ scroll_inc = scroll_dest - screen_right;
+ }
+
+ if (scroll_inc != 0)
+ {
+ set_adjustment_clamped (text_view->hadjustment,
+ current_x_scroll + scroll_inc);
+ retval = TRUE;
+ }
+
+ return retval;
+}
+
+gboolean
+gtk_text_view_scroll_to_mark (GtkTextView *text_view,
+ const gchar *mark_name,
+ gint mark_within_margin)
+{
+ g_return_val_if_fail (mark_within_margin >= 0, FALSE);
+
+ return gtk_text_view_scroll_to_mark_adjusted (text_view, mark_name,
+ mark_within_margin, 1.0);
+}
+
+static gboolean
+clamp_iter_onscreen (GtkTextView *text_view, GtkTextIter *iter)
+{
+ GdkRectangle visible_rect;
+ gtk_text_view_get_visible_rect (text_view, &visible_rect);
+
+ return gtk_text_layout_clamp_iter_to_vrange (text_view->layout, iter,
+ visible_rect.y,
+ visible_rect.y + visible_rect.height);
+}
+
+gboolean
+gtk_text_view_move_mark_onscreen (GtkTextView *text_view,
+ const gchar *mark_name)
+{
+ GtkTextIter mark;
+
+ g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
+ g_return_val_if_fail (mark_name != NULL, FALSE);
+
+ if (!gtk_text_buffer_get_iter_at_mark (text_view->buffer, &mark, mark_name))
+ return FALSE;
+
+ if (clamp_iter_onscreen (text_view, &mark))
+ {
+ gtk_text_buffer_move_mark (text_view->buffer, mark_name, &mark);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+void
+gtk_text_view_get_visible_rect (GtkTextView *text_view,
+ GdkRectangle *visible_rect)
+{
+ GtkWidget *widget;
+
+ g_return_if_fail (text_view != NULL);
+ g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
+
+ widget = GTK_WIDGET (text_view);
+
+ if (visible_rect)
+ {
+ visible_rect->x = text_view->xoffset;
+ visible_rect->y = text_view->yoffset;
+ visible_rect->width = widget->allocation.width;
+ visible_rect->height = widget->allocation.height;
+ }
+}
+
+void
+gtk_text_view_set_wrap_mode (GtkTextView *text_view,
+ GtkWrapMode wrap_mode)
+{
+ g_return_if_fail (text_view != NULL);
+ g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
+
+ if (text_view->wrap_mode != wrap_mode)
+ {
+ text_view->wrap_mode = wrap_mode;
+
+ if (text_view->layout)
+ {
+ text_view->layout->default_style->wrap_mode = wrap_mode;
+ gtk_text_layout_default_style_changed (text_view->layout);
+ }
+ }
+}
+
+GtkWrapMode
+gtk_text_view_get_wrap_mode (GtkTextView *text_view)
+{
+ g_return_val_if_fail (text_view != NULL, GTK_WRAPMODE_NONE);
+ g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), GTK_WRAPMODE_NONE);
+
+ return text_view->wrap_mode;
+}
+
+gboolean
+gtk_text_view_place_cursor_onscreen (GtkTextView *text_view)
+{
+ GtkTextIter insert;
+
+ g_return_val_if_fail (GTK_IS_TEXT_VIEW (text_view), FALSE);
+
+ gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert, "insert");
+
+ if (clamp_iter_onscreen (text_view, &insert))
+ {
+ gtk_text_buffer_place_cursor (text_view->buffer, &insert);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static void
+gtk_text_view_destroy (GtkObject *object)
+{
+ GtkTextView *text_view;
+
+ text_view = GTK_TEXT_VIEW (object);
+
+ gtk_text_view_destroy_layout (text_view);
+ gtk_text_view_set_buffer (text_view, NULL);
+
+ (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
+}
+
+static void
+gtk_text_view_finalize (GObject *object)
+{
+ GtkTextView *text_view;
+
+ text_view = GTK_TEXT_VIEW (object);
+
+ gtk_object_unref (GTK_OBJECT (text_view->hadjustment));
+ gtk_object_unref (GTK_OBJECT (text_view->vadjustment));
+ gtk_object_unref (GTK_OBJECT (text_view->im_context));
+
+ (* G_OBJECT_CLASS (parent_class)->finalize) (object);
+}
+
+static void
+gtk_text_view_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ GtkTextView *text_view;
+
+ text_view = GTK_TEXT_VIEW (object);
+
+ switch (arg_id)
+ {
+ case ARG_HEIGHT_LINES:
+ break;
+
+ case ARG_WIDTH_COLUMNS:
+ break;
+
+ case ARG_PIXELS_ABOVE_LINES:
+ break;
+
+ case ARG_PIXELS_BELOW_LINES:
+ break;
+
+ case ARG_PIXELS_INSIDE_WRAP:
+ break;
+
+ case ARG_EDITABLE:
+ break;
+
+ case ARG_WRAP_MODE:
+ break;
+
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+}
+
+static void
+gtk_text_view_get_arg (GtkObject *object, GtkArg *arg, guint arg_id)
+{
+ GtkTextView *text_view;
+
+ text_view = GTK_TEXT_VIEW (object);
+
+ switch (arg_id)
+ {
+ case ARG_HEIGHT_LINES:
+ break;
+
+ case ARG_WIDTH_COLUMNS:
+ break;
+
+ case ARG_PIXELS_ABOVE_LINES:
+ break;
+
+ case ARG_PIXELS_BELOW_LINES:
+ break;
+
+ case ARG_PIXELS_INSIDE_WRAP:
+ break;
+
+ case ARG_EDITABLE:
+ break;
+
+ case ARG_WRAP_MODE:
+ break;
+
+ default:
+ arg->type = GTK_TYPE_INVALID;
+ break;
+ }
+}
+
+static void
+gtk_text_view_size_request (GtkWidget *widget,
+ GtkRequisition *requisition)
+{
+ GtkTextView *text_view = GTK_TEXT_VIEW (widget);
+
+ /* Hrm */
+ requisition->width = 1;
+ requisition->height = 1;
+
+ /* Check to see if the widget direction has changed */
+
+ if (text_view->layout)
+ {
+ GtkTextDirection direction = gtk_widget_get_direction (widget);
+ if (direction != text_view->layout->default_style->direction)
+ {
+ text_view->layout->default_style->direction = direction;
+ gtk_text_layout_default_style_changed (text_view->layout);
+ }
+ }
+}
+
+static void
+gtk_text_view_size_allocate (GtkWidget *widget,
+ GtkAllocation *allocation)
+{
+ GtkTextView *text_view;
+ GtkTextIter first_para;
+ gint y;
+ GtkAdjustment *vadj;
+ gboolean yoffset_changed = FALSE;
+
+ text_view = GTK_TEXT_VIEW (widget);
+
+ widget->allocation = *allocation;
+
+ if (GTK_WIDGET_REALIZED (widget))
+ {
+ gdk_window_move_resize (widget->window,
+ allocation->x, allocation->y,
+ allocation->width, allocation->height);
+
+ gdk_window_resize (text_view->bin_window,
+ allocation->width, allocation->height);
+ }
+
+ gtk_text_view_ensure_layout (text_view);
+ gtk_text_layout_set_screen_width (text_view->layout,
+ GTK_WIDGET (text_view)->allocation.width);
+
+ gtk_text_view_validate_onscreen (text_view);
+ gtk_text_view_scroll_calc_now (text_view);
+
+ /* Now adjust the value of the adjustment to keep the cursor at the same place in
+ * the buffer
+ */
+ gtk_text_view_get_first_para_iter (text_view, &first_para);
+ y = gtk_text_layout_get_line_y (text_view->layout, &first_para) + text_view->first_para_pixels;
+
+ vadj = text_view->vadjustment;
+ if (y > vadj->upper - vadj->page_size)
+ y = MAX (0, vadj->upper - vadj->page_size);
+
+ if (y != text_view->yoffset)
+ {
+ vadj->value = text_view->yoffset = y;
+ yoffset_changed = TRUE;
+ }
+
+ text_view->hadjustment->page_size = allocation->width;
+ text_view->hadjustment->page_increment = allocation->width / 2;
+ text_view->hadjustment->lower = 0;
+ text_view->hadjustment->upper = MAX (allocation->width, text_view->width);
+ gtk_signal_emit_by_name (GTK_OBJECT (text_view->hadjustment), "changed");
+
+ text_view->vadjustment->page_size = allocation->height;
+ text_view->vadjustment->page_increment = allocation->height / 2;
+ text_view->vadjustment->lower = 0;
+ text_view->vadjustment->upper = MAX (allocation->height, text_view->height);
+ gtk_signal_emit_by_name (GTK_OBJECT (text_view->vadjustment), "changed");
+
+ if (yoffset_changed)
+ gtk_adjustment_value_changed (vadj);
+}
+
+static void
+gtk_text_view_get_first_para_iter (GtkTextView *text_view,
+ GtkTextIter *iter)
+{
+ gchar *mark_name = gtk_text_mark_get_name (text_view->first_para_mark);
+ gtk_text_buffer_get_iter_at_mark (text_view->buffer, iter, mark_name);
+ g_free (mark_name);
+}
+
+static void
+gtk_text_view_validate_onscreen (GtkTextView *text_view)
+{
+ GtkWidget *widget = GTK_WIDGET (text_view);
+
+ if (widget->allocation.height > 0)
+ {
+ GtkTextIter first_para;
+ gtk_text_view_get_first_para_iter (text_view, &first_para);
+ gtk_text_layout_validate_yrange (text_view->layout,
+ &first_para,
+ 0, text_view->first_para_pixels + widget->allocation.height);
+ }
+}
+
+static gboolean
+first_validate_callback (gpointer data)
+{
+ GtkTextView *text_view = data;
+
+ gtk_text_view_validate_onscreen (text_view);
+
+ text_view->first_validate_idle = 0;
+ return FALSE;
+}
+
+static gboolean
+incremental_validate_callback (gpointer data)
+{
+ GtkTextView *text_view = data;
+
+ gtk_text_layout_validate (text_view->layout, 2000);
+ if (gtk_text_layout_is_valid (text_view->layout))
+ {
+ text_view->incremental_validate_idle = 0;
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+static void
+invalidated_handler (GtkTextLayout *layout,
+ gpointer data)
+{
+ GtkTextView *text_view;
+
+ text_view = GTK_TEXT_VIEW (data);
+
+ if (!text_view->first_validate_idle)
+ text_view->first_validate_idle = g_idle_add_full (GTK_PRIORITY_RESIZE - 1, first_validate_callback, text_view, NULL);
+
+ if (!text_view->incremental_validate_idle)
+ text_view->incremental_validate_idle = g_idle_add_full (GDK_PRIORITY_REDRAW + 1, incremental_validate_callback, text_view, NULL);
+}
+
+static void
+changed_handler (GtkTextLayout *layout,
+ gint start_y,
+ gint old_height,
+ gint new_height,
+ gpointer data)
+{
+ GtkTextView *text_view;
+ GtkWidget *widget;
+ GdkRectangle visible_rect;
+ GdkRectangle redraw_rect;
+
+ text_view = GTK_TEXT_VIEW (data);
+ widget = GTK_WIDGET (data);
+
+ if (GTK_WIDGET_REALIZED (text_view))
+ {
+ gtk_text_view_get_visible_rect (text_view, &visible_rect);
+
+ redraw_rect.x = visible_rect.x;
+ redraw_rect.width = visible_rect.width;
+ redraw_rect.y = start_y;
+ redraw_rect.height = MAX (old_height, new_height);
+
+ if (gdk_rectangle_intersect (&redraw_rect, &visible_rect, &redraw_rect))
+ {
+ redraw_rect.y -= text_view->yoffset;
+ gdk_window_invalidate_rect (text_view->bin_window, &redraw_rect, FALSE);
+ }
+ }
+
+ if (old_height != new_height)
+ {
+ gboolean yoffset_changed = FALSE;
+
+ if (start_y + old_height <= text_view->yoffset - text_view->first_para_pixels)
+ {
+ text_view->yoffset += new_height - old_height;
+ text_view->vadjustment->value = text_view->yoffset;
+ yoffset_changed = TRUE;
+ }
+
+ gtk_text_view_scroll_calc_now (text_view);
+
+ if (yoffset_changed)
+ gtk_adjustment_value_changed (text_view->vadjustment);
+
+ }
+}
+
+static void
+gtk_text_view_realize (GtkWidget *widget)
+{
+ GtkTextView *text_view;
+ GdkCursor *cursor;
+ GdkWindowAttr attributes;
+ gint attributes_mask;
+
+ text_view = GTK_TEXT_VIEW (widget);
+ GTK_WIDGET_SET_FLAGS (text_view, GTK_REALIZED);
+
+ attributes.window_type = GDK_WINDOW_CHILD;
+ attributes.x = widget->allocation.x;
+ attributes.y = widget->allocation.y;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.wclass = GDK_INPUT_OUTPUT;
+ attributes.visual = gtk_widget_get_visual (widget);
+ attributes.colormap = gtk_widget_get_colormap (widget);
+ attributes.event_mask = GDK_VISIBILITY_NOTIFY_MASK;
+
+ attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
+
+ widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
+ &attributes, attributes_mask);
+ gdk_window_set_user_data (widget->window, widget);
+
+ attributes.x = 0;
+ attributes.y = 0;
+ attributes.width = widget->allocation.width;
+ attributes.height = widget->allocation.height;
+ attributes.event_mask = (GDK_EXPOSURE_MASK |
+ GDK_SCROLL_MASK |
+ GDK_KEY_PRESS_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK |
+ GDK_POINTER_MOTION_MASK |
+ GDK_POINTER_MOTION_HINT_MASK |
+ gtk_widget_get_events (widget));
+
+ text_view->bin_window = gdk_window_new (widget->window,
+ &attributes, attributes_mask);
+ gdk_window_show (text_view->bin_window);
+ gdk_window_set_user_data (text_view->bin_window, widget);
+
+ widget->style = gtk_style_attach (widget->style, widget->window);
+
+ gdk_window_set_background (text_view->bin_window, &widget->style->base[GTK_WIDGET_STATE (widget)]);
+
+ gtk_text_view_ensure_layout (text_view);
+
+ /* I-beam cursor */
+ cursor = gdk_cursor_new (GDK_XTERM);
+ gdk_window_set_cursor (text_view->bin_window, cursor);
+ gdk_cursor_destroy (cursor);
+
+ gtk_im_context_set_client_window (text_view->im_context, widget->window);
+}
+
+static void
+gtk_text_view_unrealize (GtkWidget *widget)
+{
+ GtkTextView *text_view;
+
+ text_view = GTK_TEXT_VIEW (widget);
+
+ if (text_view->first_validate_idle)
+ {
+ g_source_remove (text_view->first_validate_idle);
+ text_view->first_validate_idle = 0;
+ }
+
+ if (text_view->incremental_validate_idle)
+ {
+ g_source_remove (text_view->incremental_validate_idle);
+ text_view->incremental_validate_idle = 0;
+ }
+
+ gtk_text_view_destroy_layout (text_view);
+
+ gtk_im_context_set_client_window (text_view->im_context, NULL);
+
+ gdk_window_set_user_data (text_view->bin_window, NULL);
+ gdk_window_destroy (text_view->bin_window);
+ text_view->bin_window = NULL;
+
+ (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
+}
+
+static void
+gtk_text_view_style_set (GtkWidget *widget,
+ GtkStyle *previous_style)
+{
+ GtkTextView *text_view = GTK_TEXT_VIEW (widget);
+
+ if (GTK_WIDGET_REALIZED (widget))
+ {
+ gdk_window_set_background (text_view->bin_window,
+ &widget->style->base[GTK_WIDGET_STATE (widget)]);
+
+ gtk_text_view_set_values_from_style (text_view, text_view->layout->default_style, widget->style);
+ gtk_text_layout_default_style_changed (text_view->layout);
+ }
+}
+
+/*
+ * Events
+ */
+
+static gboolean
+get_event_coordinates (GdkEvent *event, gint *x, gint *y)
+{
+ if (event)
+ switch (event->type)
+ {
+ case GDK_MOTION_NOTIFY:
+ *x = event->motion.x;
+ *y = event->motion.y;
+ return TRUE;
+ break;
+
+ case GDK_BUTTON_PRESS:
+ case GDK_2BUTTON_PRESS:
+ case GDK_3BUTTON_PRESS:
+ case GDK_BUTTON_RELEASE:
+ *x = event->button.x;
+ *y = event->button.y;
+ return TRUE;
+ break;
+
+ case GDK_KEY_PRESS:
+ case GDK_KEY_RELEASE:
+ case GDK_ENTER_NOTIFY:
+ case GDK_LEAVE_NOTIFY:
+ case GDK_PROPERTY_NOTIFY:
+ case GDK_SELECTION_CLEAR:
+ case GDK_SELECTION_REQUEST:
+ case GDK_SELECTION_NOTIFY:
+ case GDK_PROXIMITY_IN:
+ case GDK_PROXIMITY_OUT:
+ case GDK_DRAG_ENTER:
+ case GDK_DRAG_LEAVE:
+ case GDK_DRAG_MOTION:
+ case GDK_DRAG_STATUS:
+ case GDK_DROP_START:
+ case GDK_DROP_FINISHED:
+ default:
+ return FALSE;
+ break;
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_text_view_event (GtkWidget *widget, GdkEvent *event)
+{
+ GtkTextView *text_view;
+ gint x = 0, y = 0;
+
+ text_view = GTK_TEXT_VIEW (widget);
+
+ if (text_view->layout == NULL ||
+ text_view->buffer == NULL)
+ return FALSE;
+
+ /* FIXME eventually we really want to synthesize enter/leave
+ events here as the canvas does for canvas items */
+
+ if (get_event_coordinates (event, &x, &y))
+ {
+ GtkTextIter iter;
+ gint retval = FALSE;
+
+ x += text_view->xoffset;
+ y += text_view->yoffset;
+
+ /* FIXME this is slow and we do it twice per event.
+ My favorite solution is to have GtkTextLayout cache
+ the last couple lookups. */
+ gtk_text_layout_get_iter_at_pixel (text_view->layout,
+ &iter,
+ x, y);
+
+ {
+ GSList *tags;
+ GSList *tmp;
+
+ tags = gtk_text_buffer_get_tags (text_view->buffer, &iter);
+
+ tmp = tags;
+ while (tmp != NULL)
+ {
+ GtkTextTag *tag = tmp->data;
+
+ if (gtk_text_tag_event (tag, GTK_OBJECT (widget), event, &iter))
+ {
+ retval = TRUE;
+ break;
+ }
+
+ tmp = g_slist_next (tmp);
+ }
+
+ g_slist_free (tags);
+ }
+
+ return retval;
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_text_view_key_press_event (GtkWidget *widget, GdkEventKey *event)
+{
+ GtkTextView *text_view;
+
+ text_view = GTK_TEXT_VIEW (widget);
+
+ if (text_view->layout == NULL ||
+ text_view->buffer == NULL)
+ return FALSE;
+
+ if (GTK_WIDGET_CLASS (parent_class)->key_press_event &&
+ GTK_WIDGET_CLASS (parent_class)->key_press_event (widget, event))
+ return TRUE;
+
+ if (gtk_im_context_filter_keypress (text_view->im_context, event))
+ return TRUE;
+ else if (event->keyval == GDK_Return)
+ {
+ gtk_text_buffer_insert_at_cursor (text_view->buffer, "\n", 1);
+ gtk_text_view_scroll_to_mark (text_view, "insert", 0);
+ return TRUE;
+ }
+ else
+ return FALSE;
+}
+
+static gint
+gtk_text_view_key_release_event (GtkWidget *widget, GdkEventKey *event)
+{
+ return FALSE;
+}
+
+static gint
+gtk_text_view_button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+ GtkTextView *text_view;
+
+ text_view = GTK_TEXT_VIEW (widget);
+
+ gtk_widget_grab_focus (widget);
+
+ /* debug hack */
+ if (event->button == 3 && (event->state & GDK_CONTROL_MASK) != 0)
+ gtk_text_buffer_spew (GTK_TEXT_VIEW (widget)->buffer);
+ else if (event->button == 3)
+ gtk_text_layout_spew (GTK_TEXT_VIEW (widget)->layout);
+
+ if (event->type == GDK_BUTTON_PRESS)
+ {
+ if (event->button == 1)
+ {
+ /* If we're in the selection, start a drag copy/move of the
+ selection; otherwise, start creating a new selection. */
+ GtkTextIter iter;
+ GtkTextIter start, end;
+
+ gtk_text_layout_get_iter_at_pixel (text_view->layout,
+ &iter,
+ event->x + text_view->xoffset,
+ event->y + text_view->yoffset);
+
+ if (gtk_text_buffer_get_selection_bounds (text_view->buffer,
+ &start, &end) &&
+ gtk_text_iter_in_region (&iter, &start, &end))
+ {
+ gtk_text_view_start_selection_dnd (text_view, &iter, event);
+ }
+ else
+ {
+ gtk_text_view_start_selection_drag (text_view, &iter, event);
+ }
+
+ return TRUE;
+ }
+ else if (event->button == 2)
+ {
+ GtkTextIter iter;
+
+ gtk_text_layout_get_iter_at_pixel (text_view->layout,
+ &iter,
+ event->x + text_view->xoffset,
+ event->y + text_view->yoffset);
+
+ gtk_text_buffer_paste_primary_selection (text_view->buffer,
+ &iter,
+ event->time);
+ return TRUE;
+ }
+ else if (event->button == 3)
+ {
+ if (gtk_text_view_end_selection_drag (text_view, event))
+ return TRUE;
+ else
+ return FALSE;
+ }
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_text_view_button_release_event (GtkWidget *widget, GdkEventButton *event)
+{
+ if (event->button == 1)
+ {
+ gtk_text_view_end_selection_drag (GTK_TEXT_VIEW (widget), event);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gint
+gtk_text_view_focus_in_event (GtkWidget *widget, GdkEventFocus *event)
+{
+ GtkTextMark *insert;
+
+ GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_FOCUS);
+
+ insert = gtk_text_buffer_get_mark (GTK_TEXT_VIEW (widget)->buffer,
+ "insert");
+ gtk_text_mark_set_visible (insert, TRUE);
+
+ gtk_text_view_start_cursor_blink (GTK_TEXT_VIEW (widget));
+
+ gtk_im_context_focus_in (GTK_TEXT_VIEW (widget)->im_context);
+
+ return FALSE;
+}
+
+static gint
+gtk_text_view_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
+{
+ GtkTextMark *insert;
+
+ GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_FOCUS);
+
+ insert = gtk_text_buffer_get_mark (GTK_TEXT_VIEW (widget)->buffer,
+ "insert");
+ gtk_text_mark_set_visible (insert, FALSE);
+
+ gtk_text_view_stop_cursor_blink (GTK_TEXT_VIEW (widget));
+
+ gtk_im_context_focus_out (GTK_TEXT_VIEW (widget)->im_context);
+
+ return FALSE;
+}
+
+static gint
+gtk_text_view_motion_event (GtkWidget *widget, GdkEventMotion *event)
+{
+
+ return FALSE;
+}
+
+static void
+gtk_text_view_paint (GtkWidget *widget, GdkRectangle *area)
+{
+ GtkTextView *text_view;
+
+ text_view = GTK_TEXT_VIEW (widget);
+
+ g_return_if_fail (text_view->layout != NULL);
+ g_return_if_fail (text_view->xoffset >= 0);
+ g_return_if_fail (text_view->yoffset >= 0);
+
+ gtk_text_view_validate_onscreen (text_view);
+
+#if 0
+ printf ("painting %d,%d %d x %d\n",
+ area->x, area->y,
+ area->width, area->height);
+#endif
+
+ gtk_text_layout_draw (text_view->layout,
+ widget,
+ text_view->bin_window,
+ text_view->xoffset, text_view->yoffset,
+ area->x, area->y,
+ area->width, area->height);
+}
+
+static void
+gtk_text_view_draw (GtkWidget *widget, GdkRectangle *area)
+{
+ gtk_text_view_paint (widget, area);
+}
+
+static gint
+gtk_text_view_expose_event (GtkWidget *widget, GdkEventExpose *event)
+{
+ if (event->window == GTK_TEXT_VIEW (widget)->bin_window)
+ gtk_text_view_paint (widget, &event->area);
+
+ return TRUE;
+}
+
+/*
+ * Blink!
+ */
+
+static gint
+blink_cb (gpointer data)
+{
+ GtkTextView *text_view;
+ GtkTextMark *insert;
+
+ text_view = GTK_TEXT_VIEW (data);
+
+ insert = gtk_text_buffer_get_mark (text_view->buffer,
+ "insert");
+
+ if (!GTK_WIDGET_HAS_FOCUS (text_view))
+ {
+ /* paranoia, in case the user somehow mangles our
+ focus_in/focus_out pairing. */
+ gtk_text_mark_set_visible (insert, FALSE);
+ text_view->blink_timeout = 0;
+ return FALSE;
+ }
+ else
+ {
+ gtk_text_mark_set_visible (insert,
+ !gtk_text_mark_is_visible (insert));
+ return TRUE;
+ }
+}
+
+static void
+gtk_text_view_start_cursor_blink (GtkTextView *text_view)
+{
+ return;
+ if (text_view->blink_timeout != 0)
+ return;
+
+ text_view->blink_timeout = gtk_timeout_add (500, blink_cb, text_view);
+}
+
+static void
+gtk_text_view_stop_cursor_blink (GtkTextView *text_view)
+{
+ return;
+ if (text_view->blink_timeout == 0)
+ return;
+
+ gtk_timeout_remove (text_view->blink_timeout);
+ text_view->blink_timeout = 0;
+}
+
+/*
+ * Key binding handlers
+ */
+
+static void
+gtk_text_view_move_iter_by_lines (GtkTextView *text_view,
+ GtkTextIter *newplace,
+ gint count)
+{
+ while (count < 0)
+ {
+ gtk_text_layout_move_iter_to_previous_line (text_view->layout, newplace);
+ count++;
+ }
+
+ while (count > 0)
+ {
+ gtk_text_layout_move_iter_to_next_line (text_view->layout, newplace);
+ count--;
+ }
+}
+
+static void
+gtk_text_view_move_insert (GtkTextView *text_view,
+ GtkTextViewMovementStep step,
+ gint count,
+ gboolean extend_selection)
+{
+ GtkTextIter insert;
+ GtkTextIter newplace;
+
+ gint cursor_x_pos = 0;
+
+ gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert, "insert");
+ newplace = insert;
+
+ if (step == GTK_TEXT_MOVEMENT_LINE)
+ gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, NULL);
+
+ switch (step)
+ {
+ case GTK_TEXT_MOVEMENT_CHAR:
+ gtk_text_iter_forward_chars (&newplace, count);
+ break;
+
+ case GTK_TEXT_MOVEMENT_POSITIONS:
+ gtk_text_layout_move_iter_visually (text_view->layout,
+ &newplace, count);
+ break;
+
+ case GTK_TEXT_MOVEMENT_WORD:
+ if (count < 0)
+ gtk_text_iter_backward_word_starts (&newplace, -count);
+ else if (count > 0)
+ gtk_text_iter_forward_word_ends (&newplace, count);
+ break;
+
+ case GTK_TEXT_MOVEMENT_LINE:
+ gtk_text_view_move_iter_by_lines (text_view, &newplace, count);
+ gtk_text_layout_move_iter_to_x (text_view->layout, &newplace, cursor_x_pos);
+ break;
+
+ case GTK_TEXT_MOVEMENT_PARAGRAPH:
+ /* This should almost certainly instead be doing the parallel thing to WORD */
+ gtk_text_iter_down_lines (&newplace, count);
+ break;
+
+ case GTK_TEXT_MOVEMENT_PARAGRAPH_ENDS:
+ if (count > 0)
+ gtk_text_iter_forward_to_newline (&newplace);
+ else if (count < 0)
+ gtk_text_iter_set_line_char (&newplace, 0);
+ break;
+
+ case GTK_TEXT_MOVEMENT_BUFFER_ENDS:
+ if (count > 0)
+ gtk_text_buffer_get_last_iter (text_view->buffer, &newplace);
+ else if (count < 0)
+ gtk_text_buffer_get_iter_at_char (text_view->buffer, &newplace, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ if (!gtk_text_iter_equal (&insert, &newplace))
+ {
+ if (extend_selection)
+ gtk_text_buffer_move_mark (text_view->buffer, "insert", &newplace);
+ else
+ gtk_text_buffer_place_cursor (text_view->buffer, &newplace);
+
+ gtk_text_view_scroll_to_mark (text_view, "insert", 0);
+
+ if (step == GTK_TEXT_MOVEMENT_LINE)
+ {
+ gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, -1);
+ }
+ }
+}
+
+static void
+gtk_text_view_set_anchor (GtkTextView *text_view)
+{
+ GtkTextIter insert;
+
+ gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert, "insert");
+
+ gtk_text_buffer_create_mark (text_view->buffer, "anchor", &insert, TRUE);
+}
+
+static void
+gtk_text_view_scroll_text (GtkTextView *text_view,
+ GtkTextViewScrollType type)
+{
+ gfloat newval;
+ GtkAdjustment *adj;
+ gint cursor_x_pos, cursor_y_pos;
+ GtkTextIter new_insert;
+ GtkTextIter anchor;
+ gint y0, y1;
+
+ g_return_if_fail (text_view->vadjustment != NULL);
+
+ adj = text_view->vadjustment;
+
+ /* Validate the region that will be brought into view by the cursor motion
+ */
+ switch (type)
+ {
+ default:
+ case GTK_TEXT_SCROLL_TO_TOP:
+ gtk_text_buffer_get_iter_at_char (text_view->buffer, &anchor, 0);
+ y0 = 0;
+ y1 = adj->page_size;
+ break;
+
+ case GTK_TEXT_SCROLL_TO_BOTTOM:
+ gtk_text_buffer_get_last_iter (text_view->buffer, &anchor);
+ y0 = -adj->page_size;
+ y1 = adj->page_size;
+ break;
+
+ case GTK_TEXT_SCROLL_PAGE_DOWN:
+ gtk_text_view_get_first_para_iter (text_view, &anchor);
+ y0 = adj->page_size;
+ y1 = adj->page_size + adj->page_increment;
+ break;
+
+ case GTK_TEXT_SCROLL_PAGE_UP:
+ gtk_text_view_get_first_para_iter (text_view, &anchor);
+ y0 = - adj->page_increment + adj->page_size;
+ y1 = 0;
+ break;
+ }
+ gtk_text_layout_validate_yrange (text_view->layout, &anchor, y0, y1);
+
+
+ gtk_text_view_get_virtual_cursor_pos (text_view, &cursor_x_pos, &cursor_y_pos);
+
+ newval = adj->value;
+ switch (type)
+ {
+ case GTK_TEXT_SCROLL_TO_TOP:
+ newval = adj->lower;
+ break;
+
+ case GTK_TEXT_SCROLL_TO_BOTTOM:
+ newval = adj->upper;
+ break;
+
+ case GTK_TEXT_SCROLL_PAGE_DOWN:
+ newval += adj->page_increment;
+ break;
+
+ case GTK_TEXT_SCROLL_PAGE_UP:
+ newval -= adj->page_increment;
+ break;
+
+ default:
+ break;
+ }
+
+ cursor_y_pos += newval - adj->value;
+ set_adjustment_clamped (adj, newval);
+
+ gtk_text_layout_get_iter_at_pixel (text_view->layout, &new_insert, cursor_x_pos, cursor_y_pos);
+ clamp_iter_onscreen (text_view, &new_insert);
+ gtk_text_buffer_place_cursor (text_view->buffer, &new_insert);
+
+ gtk_text_view_set_virtual_cursor_pos (text_view, cursor_x_pos, cursor_y_pos);
+
+ /* Adjust to have the cursor _entirely_ onscreen, move_mark_onscreen
+ * only guarantees 1 pixel onscreen.
+ */
+ gtk_text_view_scroll_to_mark (text_view, "insert", 0);
+}
+
+static gboolean
+whitespace (GtkTextUniChar ch, gpointer user_data)
+{
+ return (ch == ' ' || ch == '\t');
+}
+
+static gboolean
+not_whitespace (GtkTextUniChar ch, gpointer user_data)
+{
+ return !whitespace (ch, user_data);
+}
+
+static gboolean
+find_whitepace_region (const GtkTextIter *center,
+ GtkTextIter *start, GtkTextIter *end)
+{
+ *start = *center;
+ *end = *center;
+
+ if (gtk_text_iter_backward_find_char (start, not_whitespace, NULL))
+ gtk_text_iter_forward_char (start); /* we want the first whitespace... */
+ if (whitespace (gtk_text_iter_get_char (end), NULL))
+ gtk_text_iter_forward_find_char (end, not_whitespace, NULL);
+
+ return !gtk_text_iter_equal (start, end);
+}
+
+static void
+gtk_text_view_delete_text (GtkTextView *text_view,
+ GtkTextViewDeleteType type,
+ gint count)
+{
+ GtkTextIter insert;
+ GtkTextIter start;
+ GtkTextIter end;
+ gboolean leave_one = FALSE;
+
+ if (type == GTK_TEXT_DELETE_CHAR)
+ {
+ /* Char delete deletes the selection, if one exists */
+ if (gtk_text_buffer_delete_selection (text_view->buffer))
+ return;
+ }
+
+ gtk_text_buffer_get_iter_at_mark (text_view->buffer,
+ &insert,
+ "insert");
+
+ start = insert;
+ end = insert;
+
+ switch (type)
+ {
+ case GTK_TEXT_DELETE_CHAR:
+ gtk_text_iter_forward_chars (&end, count);
+ break;
+
+ case GTK_TEXT_DELETE_HALF_WORD:
+ if (count > 0)
+ gtk_text_iter_forward_word_ends (&end, count);
+ else if (count < 0)
+ gtk_text_iter_backward_word_starts (&start, 0 - count);
+ break;
+
+ case GTK_TEXT_DELETE_WHOLE_WORD:
+ break;
+
+ case GTK_TEXT_DELETE_HALF_LINE:
+ break;
+
+ case GTK_TEXT_DELETE_WHOLE_LINE:
+ break;
+
+ case GTK_TEXT_DELETE_HALF_PARAGRAPH:
+ while (count > 0)
+ {
+ if (!gtk_text_iter_forward_to_newline (&end))
+ break;
+
+ --count;
+ }
+
+ /* FIXME figure out what a negative count means
+ and support that */
+ break;
+
+ case GTK_TEXT_DELETE_WHOLE_PARAGRAPH:
+ if (count > 0)
+ {
+ gtk_text_iter_set_line_char (&start, 0);
+ gtk_text_iter_forward_to_newline (&end);
+
+ /* Do the lines beyond the first. */
+ while (count > 1)
+ {
+ gtk_text_iter_forward_to_newline (&end);
+
+ --count;
+ }
+ }
+
+ /* FIXME negative count? */
+
+ break;
+
+ case GTK_TEXT_DELETE_WHITESPACE_LEAVE_ONE:
+ leave_one = TRUE; /* FALL THRU */
+ case GTK_TEXT_DELETE_WHITESPACE:
+ {
+ find_whitepace_region (&insert, &start, &end);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (!gtk_text_iter_equal (&start, &end))
+ {
+ gtk_text_buffer_delete (text_view->buffer, &start, &end);
+
+ if (leave_one)
+ gtk_text_buffer_insert_at_cursor (text_view->buffer, " ", 1);
+
+ gtk_text_view_scroll_to_mark (text_view, "insert", 0);
+ }
+}
+
+static void
+gtk_text_view_cut_text (GtkTextView *text_view)
+{
+ gtk_text_buffer_cut (text_view->buffer, GDK_CURRENT_TIME);
+ gtk_text_view_scroll_to_mark (text_view, "insert", 0);
+}
+
+static void
+gtk_text_view_copy_text (GtkTextView *text_view)
+{
+ gtk_text_buffer_copy (text_view->buffer, GDK_CURRENT_TIME);
+ gtk_text_view_scroll_to_mark (text_view, "insert", 0);
+}
+
+static void
+gtk_text_view_paste_text (GtkTextView *text_view)
+{
+ gtk_text_buffer_paste_clipboard (text_view->buffer, GDK_CURRENT_TIME);
+ gtk_text_view_scroll_to_mark (text_view, "insert", 0);
+}
+
+static void
+gtk_text_view_toggle_overwrite (GtkTextView *text_view)
+{
+ text_view->overwrite_mode = !text_view->overwrite_mode;
+}
+
+/*
+ * Selections
+ */
+
+static gboolean
+move_insert_to_pointer_and_scroll (GtkTextView *text_view, gboolean partial_scroll)
+{
+ gint x, y;
+ GdkModifierType state;
+ GtkTextIter newplace;
+ gint adjust = 0;
+ gboolean in_threshold = FALSE;
+
+ gdk_window_get_pointer (text_view->bin_window, &x, &y, &state);
+
+ /* Adjust movement by how long we've been selecting, to
+ get an acceleration effect. The exact numbers are
+ pretty arbitrary. We have a threshold before we
+ start to accelerate. */
+ /* uncommenting this printf helps visualize how it works. */
+ /* printf ("%d\n", text_view->scrolling_accel_factor); */
+
+ if (text_view->scrolling_accel_factor > 10)
+ adjust = (text_view->scrolling_accel_factor - 10) * 75;
+
+ if (y < 0) /* scrolling upward */
+ adjust = -adjust;
+
+ /* No adjust if the pointer has moved back inside the window for sure.
+ Also I'm adding a small threshold where no adjust is added,
+ in case you want to do a continuous slow scroll. */
+#define SLOW_SCROLL_TH 7
+ if (x >= (0 - SLOW_SCROLL_TH) &&
+ x < (GTK_WIDGET (text_view)->allocation.width + SLOW_SCROLL_TH) &&
+ y >= (0 - SLOW_SCROLL_TH) &&
+ y < (GTK_WIDGET (text_view)->allocation.height + SLOW_SCROLL_TH))
+ {
+ adjust = 0;
+ in_threshold = TRUE;
+ }
+
+ gtk_text_layout_get_iter_at_pixel (text_view->layout,
+ &newplace,
+ x + text_view->xoffset,
+ y + text_view->yoffset + adjust);
+
+ {
+ gboolean scrolled = FALSE;
+
+ gtk_text_buffer_move_mark (text_view->buffer,
+ "insert",
+ &newplace);
+
+ if (partial_scroll)
+ scrolled = gtk_text_view_scroll_to_mark_adjusted (text_view, "insert", 0, 0.7);
+ else
+ scrolled = gtk_text_view_scroll_to_mark_adjusted (text_view, "insert", 0, 1.0);
+
+ if (scrolled)
+ {
+ /* We want to avoid rapid jump to super-accelerated when you
+ leave the slow scroll threshold after scrolling for a
+ while. So we slowly decrease accel when scrolling inside
+ the threshold.
+ */
+ if (in_threshold)
+ {
+ if (text_view->scrolling_accel_factor > 1)
+ text_view->scrolling_accel_factor -= 2;
+ }
+ else
+ text_view->scrolling_accel_factor += 1;
+ }
+ else
+ {
+ /* If we don't scroll we're probably inside the window, but
+ potentially just a bit outside. We decrease acceleration
+ while the user is fooling around inside the window.
+ Acceleration decreases faster than it increases. */
+ if (text_view->scrolling_accel_factor > 4)
+ text_view->scrolling_accel_factor -= 5;
+ }
+
+ return scrolled;
+ }
+}
+
+static gint
+selection_scan_timeout (gpointer data)
+{
+ GtkTextView *text_view;
+
+ text_view = GTK_TEXT_VIEW (data);
+
+ if (move_insert_to_pointer_and_scroll (text_view, TRUE))
+ {
+ return TRUE; /* remain installed. */
+ }
+ else
+ {
+ text_view->selection_drag_scan_timeout = 0;
+ return FALSE; /* remove ourselves */
+ }
+}
+
+static gint
+selection_motion_event_handler (GtkTextView *text_view, GdkEventMotion *event, gpointer data)
+{
+ if (move_insert_to_pointer_and_scroll (text_view, TRUE))
+ {
+ /* If we had to scroll offscreen, insert a timeout to do so
+ again. Note that in the timeout, even if the mouse doesn't
+ move, due to this scroll xoffset/yoffset will have changed
+ and we'll need to scroll again. */
+ if (text_view->selection_drag_scan_timeout != 0) /* reset on every motion event */
+ gtk_timeout_remove (text_view->selection_drag_scan_timeout);
+
+ text_view->selection_drag_scan_timeout =
+ gtk_timeout_add (50, selection_scan_timeout, text_view);
+ }
+
+ return TRUE;
+}
+
+static void
+gtk_text_view_start_selection_drag (GtkTextView *text_view,
+ const GtkTextIter *iter,
+ GdkEventButton *event)
+{
+ GtkTextIter newplace;
+
+ g_return_if_fail (text_view->selection_drag_handler == 0);
+
+ gtk_grab_add (GTK_WIDGET (text_view));
+
+ text_view->scrolling_accel_factor = 0;
+
+ newplace = *iter;
+
+ gtk_text_buffer_place_cursor (text_view->buffer, &newplace);
+
+ text_view->selection_drag_handler = gtk_signal_connect (GTK_OBJECT (text_view),
+ "motion_notify_event",
+ GTK_SIGNAL_FUNC (selection_motion_event_handler),
+ NULL);
+}
+
+/* returns whether we were really dragging */
+static gboolean
+gtk_text_view_end_selection_drag (GtkTextView *text_view, GdkEventButton *event)
+{
+ if (text_view->selection_drag_handler == 0)
+ return FALSE;
+
+ gtk_signal_disconnect (GTK_OBJECT (text_view), text_view->selection_drag_handler);
+ text_view->selection_drag_handler = 0;
+
+ text_view->scrolling_accel_factor = 0;
+
+ if (text_view->selection_drag_scan_timeout != 0)
+ {
+ gtk_timeout_remove (text_view->selection_drag_scan_timeout);
+ text_view->selection_drag_scan_timeout = 0;
+ }
+
+ /* one last update to current position */
+ move_insert_to_pointer_and_scroll (text_view, FALSE);
+
+ gtk_grab_remove (GTK_WIDGET (text_view));
+
+ return TRUE;
+}
+
+/*
+ * Layout utils
+ */
+
+static void
+gtk_text_view_set_adjustment_upper (GtkAdjustment *adj, gfloat upper)
+{
+ if (upper != adj->upper)
+ {
+ gfloat min = MAX (0., upper - adj->page_size);
+ gboolean value_changed = FALSE;
+
+ adj->upper = upper;
+
+ if (adj->value > min)
+ {
+ adj->value = min;
+ value_changed = TRUE;
+ }
+
+ gtk_signal_emit_by_name (GTK_OBJECT (adj), "changed");
+ if (value_changed)
+ gtk_signal_emit_by_name (GTK_OBJECT (adj), "value_changed");
+ }
+}
+
+static void
+gtk_text_view_scroll_calc_now (GtkTextView *text_view)
+{
+ gint width = 0, height = 0;
+ GtkWidget *widget = GTK_WIDGET (text_view);
+
+ gtk_text_view_ensure_layout (text_view);
+
+
+ gtk_text_layout_set_screen_width (text_view->layout,
+ widget->allocation.width);
+
+ gtk_text_layout_get_size (text_view->layout, &width, &height);
+
+#if 0
+ /* If the width is less than the screen width (likely
+ if we have wrapping turned on for the whole widget),
+ then we want to set the scroll region to the screen
+ width. If the width is greater (wrapping off) then we
+ probably want to set the scroll region to the width
+ of the layout. I guess.
+ */
+
+ width = MAX (text_view->layout->screen_width, width);
+ height = height;
+#endif
+
+ if (text_view->width != width || text_view->height != height)
+ {
+#if 0
+ printf ("layout size set, widget width is %d\n",
+ GTK_WIDGET (text_view)->allocation.width);
+#endif
+ text_view->width = width;
+ text_view->height = height;
+
+ gtk_text_view_set_adjustment_upper (text_view->hadjustment,
+ MAX (widget->allocation.width, width));
+ gtk_text_view_set_adjustment_upper (text_view->vadjustment,
+ MAX (widget->allocation.height, height));
+
+ /* Set up the step sizes; we'll say that a page is
+ our allocation minus one step, and a step is
+ 1/10 of our allocation. */
+ text_view->hadjustment->step_increment =
+ GTK_WIDGET (text_view)->allocation.width/10.0;
+ text_view->hadjustment->page_increment =
+ GTK_WIDGET (text_view)->allocation.width *0.9;
+
+ text_view->vadjustment->step_increment =
+ GTK_WIDGET (text_view)->allocation.height/10.0;
+ text_view->vadjustment->page_increment =
+ GTK_WIDGET (text_view)->allocation.height *0.9;
+ }
+}
+
+static void
+gtk_text_view_set_values_from_style (GtkTextView *text_view,
+ GtkTextStyleValues *values,
+ GtkStyle *style)
+{
+ values->appearance.bg_color = style->base[GTK_STATE_NORMAL];
+ values->appearance.fg_color = style->fg[GTK_STATE_NORMAL];
+
+ if (values->font_desc)
+ pango_font_description_free (values->font_desc);
+
+ values->font_desc = pango_font_description_copy (style->font_desc);
+}
+
+static void
+gtk_text_view_ensure_layout (GtkTextView *text_view)
+{
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (text_view);
+
+ if (text_view->layout == NULL)
+ {
+ GtkTextStyleValues *style;
+ PangoContext *ltr_context, *rtl_context;
+
+ text_view->layout = gtk_text_layout_new ();
+
+ gtk_signal_connect (GTK_OBJECT (text_view->layout),
+ "invalidated",
+ GTK_SIGNAL_FUNC (invalidated_handler),
+ text_view);
+
+ gtk_signal_connect (GTK_OBJECT (text_view->layout),
+ "changed",
+ GTK_SIGNAL_FUNC (changed_handler),
+ text_view);
+
+ if (text_view->buffer)
+ gtk_text_layout_set_buffer (text_view->layout, text_view->buffer);
+
+ ltr_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
+ pango_context_set_base_dir (ltr_context, PANGO_DIRECTION_LTR);
+ rtl_context = gtk_widget_create_pango_context (GTK_WIDGET (text_view));
+ pango_context_set_base_dir (rtl_context, PANGO_DIRECTION_RTL);
+
+ gtk_text_layout_set_contexts (text_view->layout, ltr_context, rtl_context);
+
+ pango_context_unref (ltr_context);
+ pango_context_unref (rtl_context);
+
+ style = gtk_text_view_style_values_new ();
+
+ gtk_widget_ensure_style (widget);
+ gtk_text_view_set_values_from_style (text_view, style, widget->style);
+
+ style->pixels_above_lines = 2;
+ style->pixels_below_lines = 2;
+ style->pixels_inside_wrap = 1;
+
+ style->wrap_mode = text_view->wrap_mode;
+ style->justify = GTK_JUSTIFY_LEFT;
+ style->direction = gtk_widget_get_direction (GTK_WIDGET (text_view));
+
+ gtk_text_layout_set_default_style (text_view->layout, style);
+
+ gtk_text_view_style_values_unref (style);
+ }
+}
+
+static void
+gtk_text_view_destroy_layout (GtkTextView *text_view)
+{
+ if (text_view->layout)
+ {
+ gtk_text_view_end_selection_drag (text_view, NULL);
+
+ gtk_signal_disconnect_by_func (GTK_OBJECT (text_view->layout),
+ invalidated_handler, text_view);
+ gtk_signal_disconnect_by_func (GTK_OBJECT (text_view->layout),
+ changed_handler, text_view);
+ gtk_object_unref (GTK_OBJECT (text_view->layout));
+ text_view->layout = NULL;
+ }
+}
+
+
+/*
+ * DND feature
+ */
+
+static void
+gtk_text_view_start_selection_dnd (GtkTextView *text_view,
+ const GtkTextIter *iter,
+ GdkEventButton *event)
+{
+ GdkDragContext *context;
+ GtkTargetList *target_list;
+
+ /* FIXME we have to handle more formats for the selection,
+ and do the conversions to/from UTF8 */
+
+ /* FIXME not sure how this is memory-managed. */
+ target_list = gtk_target_list_new (target_table, n_targets);
+
+ context = gtk_drag_begin (GTK_WIDGET (text_view), target_list,
+ GDK_ACTION_COPY | GDK_ACTION_MOVE,
+ 1, (GdkEvent*)event);
+
+ gtk_drag_set_icon_default (context);
+
+ /* We're inside the selection, so start without being able
+ to accept the drag. */
+ gdk_drag_status (context, 0, event->time);
+ gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
+}
+
+static void
+gtk_text_view_drag_begin (GtkWidget *widget,
+ GdkDragContext *context)
+{
+
+}
+
+static void
+gtk_text_view_drag_end (GtkWidget *widget,
+ GdkDragContext *context)
+{
+ GtkTextView *text_view;
+
+ text_view = GTK_TEXT_VIEW (widget);
+
+ gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
+}
+
+static void
+gtk_text_view_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time)
+{
+ gchar *str;
+ gint length;
+ GtkTextIter start;
+ GtkTextIter end;
+ GtkTextView *text_view;
+
+ text_view = GTK_TEXT_VIEW (widget);
+
+ str = NULL;
+ length = 0;
+
+ if (gtk_text_buffer_get_selection_bounds (text_view->buffer, &start, &end))
+ {
+ /* Extract the selected text */
+ str = gtk_text_iter_get_visible_text (&start, &end);
+
+ length = strlen (str);
+ }
+
+ if (str)
+ {
+ if (info == TARGET_UTF8_STRING)
+ {
+ /* Pass raw UTF8 */
+ gtk_selection_data_set (selection_data,
+ utf8_atom,
+ 8*sizeof (gchar), (guchar *)str, length);
+
+ }
+ else if (info == TARGET_STRING ||
+ info == TARGET_TEXT)
+ {
+ gchar *latin1;
+
+ latin1 = gtk_text_utf_to_latin1(str, length);
+
+ gtk_selection_data_set (selection_data,
+ GDK_SELECTION_TYPE_STRING,
+ 8*sizeof (gchar), latin1, strlen (latin1));
+ g_free (latin1);
+ }
+ else if (info == TARGET_COMPOUND_TEXT)
+ {
+ /* FIXME convert UTF8 directly to current locale, not via
+ latin1 */
+
+ guchar *text;
+ GdkAtom encoding;
+ gint format;
+ gint new_length;
+ gchar *latin1;
+
+ latin1 = gtk_text_utf_to_latin1(str, length);
+
+ gdk_string_to_compound_text (latin1, &encoding, &format, &text, &new_length);
+ gtk_selection_data_set (selection_data, encoding, format, text, new_length);
+ gdk_free_compound_text (text);
+
+ g_free (latin1);
+ }
+
+ g_free (str);
+ }
+}
+
+static void
+gtk_text_view_drag_data_delete (GtkWidget *widget,
+ GdkDragContext *context)
+{
+ gtk_text_buffer_delete_selection (GTK_TEXT_VIEW (widget)->buffer);
+}
+
+static void
+gtk_text_view_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time)
+{
+
+
+}
+
+static gboolean
+gtk_text_view_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+ GtkTextIter newplace;
+ GtkTextView *text_view;
+ GtkTextIter start;
+ GtkTextIter end;
+
+ text_view = GTK_TEXT_VIEW (widget);
+
+ gtk_text_layout_get_iter_at_pixel (text_view->layout,
+ &newplace,
+ x + text_view->xoffset,
+ y + text_view->yoffset);
+
+ if (gtk_text_buffer_get_selection_bounds (text_view->buffer,
+ &start, &end) &&
+ gtk_text_iter_in_region (&newplace, &start, &end))
+ {
+ /* We're inside the selection. */
+ gdk_drag_status (context, 0, time);
+ gtk_text_mark_set_visible (text_view->dnd_mark, FALSE);
+ }
+ else
+ {
+ gtk_text_mark_set_visible (text_view->dnd_mark, TRUE);
+
+ gdk_drag_status (context, context->suggested_action, time);
+ }
+
+ gtk_text_buffer_move_mark (text_view->buffer,
+ "__drag_target",
+ &newplace);
+
+ {
+ /* The effect of this is that the text scrolls if you're near
+ the edge. We have to scroll whether or not we're inside
+ the selection. */
+ gint margin;
+
+ margin = MIN (widget->allocation.width, widget->allocation.height);
+ margin /= 5;
+
+ gtk_text_view_scroll_to_mark_adjusted (text_view, "__drag_target", margin, 1.0);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gtk_text_view_drag_drop (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time)
+{
+#if 0
+ /* called automatically. */
+ if (context->targets)
+ {
+ gtk_drag_get_data (widget, context,
+ GPOINTER_TO_INT (context->targets->data),
+ time);
+ return TRUE;
+ }
+ else
+ return FALSE;
+#endif
+ return TRUE;
+}
+
+static void
+gtk_text_view_drag_data_received (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time)
+{
+ GtkTextIter drop_point;
+ GtkTextView *text_view;
+ enum {INVALID, STRING, CTEXT, UTF8} type;
+
+ text_view = GTK_TEXT_VIEW (widget);
+
+ if (selection_data->type == GDK_TARGET_STRING)
+ type = STRING;
+ else if (selection_data->type == ctext_atom)
+ type = CTEXT;
+ else if (selection_data->type == utf8_atom)
+ type = UTF8;
+ else
+ type = INVALID;
+
+ if (type == INVALID || selection_data->length < 0)
+ {
+ /* I think the DND code automatically tries asking
+ for the various formats. */
+ return;
+ }
+
+ if (!gtk_text_buffer_get_iter_at_mark (text_view->buffer, &drop_point, "__drag_target"))
+ return;
+
+ switch (type)
+ {
+ case STRING:
+ {
+ gchar *utf;
+
+ utf = gtk_text_latin1_to_utf ((const gchar*)selection_data->data,
+ selection_data->length);
+ gtk_text_buffer_insert (text_view->buffer, &drop_point,
+ utf, -1);
+ g_free (utf);
+ }
+ break;
+
+ case UTF8:
+ gtk_text_buffer_insert (text_view->buffer, &drop_point,
+ (const gchar *)selection_data->data,
+ selection_data->length);
+ break;
+
+ case CTEXT:
+ {
+ gchar **list;
+ gint count;
+ gint i;
+
+ count = gdk_text_property_to_text_list (selection_data->type,
+ selection_data->format,
+ selection_data->data,
+ selection_data->length,
+ &list);
+ for (i=0; i<count; i++)
+ {
+ /* FIXME this is broken, it assumes the CTEXT is latin1
+ when it probably isn't. */
+ gchar *utf;
+
+ utf = gtk_text_latin1_to_utf (list[i], strlen (list[i]));
+
+ gtk_text_buffer_insert (text_view->buffer, &drop_point, utf, -1);
+
+ g_free (utf);
+ }
+
+ if (count > 0)
+ gdk_free_text_list (list);
+ }
+ break;
+
+ case INVALID: /* quiet compiler */
+ break;
+ }
+}
+
+static void
+gtk_text_view_set_scroll_adjustments (GtkTextView *text_view,
+ GtkAdjustment *hadj,
+ GtkAdjustment *vadj)
+{
+ gboolean need_adjust = FALSE;
+
+ g_return_if_fail (text_view != NULL);
+ g_return_if_fail (GTK_IS_TEXT_VIEW (text_view));
+
+ if (hadj)
+ g_return_if_fail (GTK_IS_ADJUSTMENT (hadj));
+ else
+ hadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
+ if (vadj)
+ g_return_if_fail (GTK_IS_ADJUSTMENT (vadj));
+ else
+ vadj = GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
+
+ if (text_view->hadjustment && (text_view->hadjustment != hadj))
+ {
+ gtk_signal_disconnect_by_data (GTK_OBJECT (text_view->hadjustment), text_view);
+ gtk_object_unref (GTK_OBJECT (text_view->hadjustment));
+ }
+
+ if (text_view->vadjustment && (text_view->vadjustment != vadj))
+ {
+ gtk_signal_disconnect_by_data (GTK_OBJECT (text_view->vadjustment), text_view);
+ gtk_object_unref (GTK_OBJECT (text_view->vadjustment));
+ }
+
+ if (text_view->hadjustment != hadj)
+ {
+ text_view->hadjustment = hadj;
+ gtk_object_ref (GTK_OBJECT (text_view->hadjustment));
+ gtk_object_sink (GTK_OBJECT (text_view->hadjustment));
+
+ gtk_signal_connect (GTK_OBJECT (text_view->hadjustment), "value_changed",
+ (GtkSignalFunc) gtk_text_view_value_changed,
+ text_view);
+ need_adjust = TRUE;
+ }
+
+ if (text_view->vadjustment != vadj)
+ {
+ text_view->vadjustment = vadj;
+ gtk_object_ref (GTK_OBJECT (text_view->vadjustment));
+ gtk_object_sink (GTK_OBJECT (text_view->vadjustment));
+
+ gtk_signal_connect (GTK_OBJECT (text_view->vadjustment), "value_changed",
+ (GtkSignalFunc) gtk_text_view_value_changed,
+ text_view);
+ need_adjust = TRUE;
+ }
+
+ if (need_adjust)
+ gtk_text_view_value_changed (NULL, text_view);
+}
+
+static void
+gtk_text_view_value_changed (GtkAdjustment *adj,
+ GtkTextView *text_view)
+{
+ GtkTextIter iter;
+ gchar *mark_name;
+ gint line_top;
+ gint dx = 0;
+ gint dy = 0;
+
+ if (adj == text_view->hadjustment)
+ {
+ dx = text_view->xoffset - (gint)adj->value;
+ text_view->xoffset = adj->value;
+ }
+ else if (adj == text_view->vadjustment)
+ {
+ dy = text_view->yoffset - (gint)adj->value;
+ text_view->yoffset = adj->value;
+
+ if (text_view->layout)
+ {
+ gtk_text_layout_get_line_at_y (text_view->layout, &iter, adj->value, &line_top);
+
+ mark_name = gtk_text_mark_get_name (text_view->first_para_mark);
+ gtk_text_buffer_move_mark (text_view->buffer, mark_name, &iter);
+ g_free (mark_name);
+
+ text_view->first_para_pixels = adj->value - line_top;
+ }
+ }
+
+ if (dx != 0 || dy != 0)
+ {
+ gdk_window_scroll (text_view->bin_window, dx, dy);
+ gdk_window_process_updates (text_view->bin_window, TRUE);
+ }
+}
+
+static void
+gtk_text_view_commit_handler (GtkIMContext *context,
+ const gchar *str,
+ GtkTextView *text_view)
+{
+ gtk_text_buffer_delete_selection (text_view->buffer);
+
+ if (!strcmp (str, "\n"))
+ {
+ gtk_text_buffer_insert_at_cursor (text_view->buffer, "\n", 1);
+ }
+ else
+ {
+ if (text_view->overwrite_mode)
+ gtk_text_view_delete_text (text_view, GTK_TEXT_DELETE_CHAR, 1);
+ gtk_text_buffer_insert_at_cursor (text_view->buffer, str, strlen (str));
+ }
+
+ gtk_text_view_scroll_to_mark (text_view, "insert", 0);
+}
+
+static void
+gtk_text_view_mark_set_handler (GtkTextBuffer *buffer,
+ const GtkTextIter *location,
+ const char *mark_name,
+ gpointer data)
+{
+ GtkTextView *text_view = GTK_TEXT_VIEW (data);
+
+ if (!strcmp (mark_name, "insert"))
+ {
+ text_view->virtual_cursor_x = -1;
+ text_view->virtual_cursor_y = -1;
+ }
+}
+
+static void
+gtk_text_view_get_virtual_cursor_pos (GtkTextView *text_view,
+ gint *x,
+ gint *y)
+{
+ GdkRectangle strong_pos;
+ GtkTextIter insert;
+
+ gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert, "insert");
+
+ if ((x && text_view->virtual_cursor_x == -1) ||
+ (y && text_view->virtual_cursor_y == -1))
+ gtk_text_layout_get_cursor_locations (text_view->layout, &insert, &strong_pos, NULL);
+
+ if (x)
+ {
+ if (text_view->virtual_cursor_x != -1)
+ *x = text_view->virtual_cursor_x;
+ else
+ *x = strong_pos.x;
+ }
+
+ if (y)
+ {
+ if (text_view->virtual_cursor_x != -1)
+ *y = text_view->virtual_cursor_y;
+ else
+ *y = strong_pos.y + strong_pos.height / 2;
+ }
+}
+
+static void
+gtk_text_view_set_virtual_cursor_pos (GtkTextView *text_view,
+ gint x,
+ gint y)
+{
+ GdkRectangle strong_pos;
+ GtkTextIter insert;
+
+ gtk_text_buffer_get_iter_at_mark (text_view->buffer, &insert, "insert");
+
+ if (x == -1 || y == -1)
+ gtk_text_layout_get_cursor_locations (text_view->layout, &insert, &strong_pos, NULL);
+
+ text_view->virtual_cursor_x = (x == -1) ? strong_pos.x : x;
+ text_view->virtual_cursor_y = (y == -1) ? strong_pos.y + strong_pos.height / 2 : y;
+}
diff --git a/gtk/gtktextview.h b/gtk/gtktextview.h
new file mode 100644
index 000000000..ba46ae6b4
--- /dev/null
+++ b/gtk/gtktextview.h
@@ -0,0 +1,151 @@
+#ifndef GTK_TEXT_VIEW_H
+#define GTK_TEXT_VIEW_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <gtk/gtkcontainer.h>
+#include <gtk/gtkimcontext.h>
+#include <gtk/gtktextbuffer.h>
+
+typedef enum {
+ GTK_TEXT_MOVEMENT_CHAR, /* move by forw/back chars */
+ GTK_TEXT_MOVEMENT_POSITIONS, /* move by left/right chars */
+ GTK_TEXT_MOVEMENT_WORD, /* move by forward/back words */
+ GTK_TEXT_MOVEMENT_LINE, /* move up/down lines (wrapped lines) */
+ GTK_TEXT_MOVEMENT_PARAGRAPH, /* move up/down paragraphs (newline-ended lines) */
+ GTK_TEXT_MOVEMENT_PARAGRAPH_ENDS, /* move to either end of a paragraph */
+ GTK_TEXT_MOVEMENT_BUFFER_ENDS /* move to ends of the buffer */
+} GtkTextViewMovementStep;
+
+typedef enum {
+ GTK_TEXT_SCROLL_TO_TOP,
+ GTK_TEXT_SCROLL_TO_BOTTOM,
+ GTK_TEXT_SCROLL_PAGE_DOWN,
+ GTK_TEXT_SCROLL_PAGE_UP
+} GtkTextViewScrollType;
+
+typedef enum {
+ GTK_TEXT_DELETE_CHAR,
+ GTK_TEXT_DELETE_HALF_WORD, /* delete only the portion of the word to the
+ left/right of cursor if we're in the middle
+ of a word */
+ GTK_TEXT_DELETE_WHOLE_WORD,
+ GTK_TEXT_DELETE_HALF_LINE,
+ GTK_TEXT_DELETE_WHOLE_LINE,
+ GTK_TEXT_DELETE_HALF_PARAGRAPH, /* like C-k in Emacs (or its reverse) */
+ GTK_TEXT_DELETE_WHOLE_PARAGRAPH, /* C-k in pico, kill whole line */
+ GTK_TEXT_DELETE_WHITESPACE, /* M-\ in Emacs */
+ GTK_TEXT_DELETE_WHITESPACE_LEAVE_ONE /* M-space in Emacs */
+} GtkTextViewDeleteType;
+
+#define GTK_TYPE_TEXT_VIEW (gtk_text_view_get_type())
+#define GTK_TEXT_VIEW(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_TEXT_VIEW, GtkTextView))
+#define GTK_TEXT_VIEW_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_TEXT_VIEW, GtkTextViewClass))
+#define GTK_IS_TEXT_VIEW(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_TEXT_VIEW))
+#define GTK_IS_TEXT_VIEW_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TEXT_VIEW))
+#define GTK_TEXT_VIEW_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_TEXT_VIEW, GtkTextViewClass))
+
+typedef struct _GtkTextView GtkTextView;
+typedef struct _GtkTextViewClass GtkTextViewClass;
+
+struct _GtkTextView {
+ GtkContainer parent_instance;
+
+ struct _GtkTextLayout *layout;
+ GtkTextBuffer *buffer;
+
+ guint selection_drag_handler;
+ guint selection_drag_scan_timeout;
+ gint scrolling_accel_factor;
+
+ gboolean overwrite_mode;
+
+ GtkWrapMode wrap_mode; /* Default wrap mode */
+
+ GdkWindow *bin_window;
+ GtkAdjustment *hadjustment;
+ GtkAdjustment *vadjustment;
+
+ gint xoffset; /* Offsets between widget coordinates and buffer coordinates */
+ gint yoffset;
+ gint width; /* Width and height of the buffer */
+ gint height;
+
+ /* The virtual cursor position is normally the same as the
+ * actual (strong) cursor position, except in two circumstances:
+ *
+ * a) When the cursor is moved vertically with the keyboard
+ * b) When the text view is scrolled with the keyboard
+ *
+ * In case a), virtual_cursor_x is preserved, but not virtual_cursor_y
+ * In case b), both virtual_cursor_x and virtual_cursor_y are preserved.
+ */
+ gint virtual_cursor_x; /* -1 means use actual cursor position */
+ gint virtual_cursor_y; /* -1 means use actual cursor position */
+
+ GtkTextMark *first_para_mark; /* Mark at the beginning of the first onscreen paragraph */
+ gint first_para_pixels; /* Offset of top of screen in the first onscreen paragraph */
+
+ GtkTextMark *dnd_mark;
+ guint blink_timeout;
+
+ guint first_validate_idle; /* Idle to revalidate onscreen portion, runs before resize */
+ guint incremental_validate_idle; /* Idle to revalidate offscreen portions, runs after redraw */
+
+ GtkIMContext *im_context;
+};
+
+struct _GtkTextViewClass {
+ GtkContainerClass parent_class;
+
+ /* These are all RUN_ACTION signals for keybindings */
+
+ /* move insertion point */
+ void (* move_insert) (GtkTextView *text_view, GtkTextViewMovementStep step, gint count, gboolean extend_selection);
+ /* move the "anchor" (what Emacs calls the mark) to the cursor position */
+ void (* set_anchor) (GtkTextView *text_view);
+ /* Scroll */
+ void (* scroll_text) (GtkTextView *text_view, GtkTextViewScrollType type);
+ /* Deletions */
+ void (* delete_text) (GtkTextView *text_view, GtkTextViewDeleteType type, gint count);
+ /* cut copy paste */
+ void (* cut_text) (GtkTextView *text_view);
+ void (* copy_text) (GtkTextView *text_view);
+ void (* paste_text) (GtkTextView *text_view);
+ /* overwrite */
+ void (* toggle_overwrite) (GtkTextView *text_view);
+ void (*set_scroll_adjustments) (GtkTextView *text_view,
+ GtkAdjustment *hadjustment,
+ GtkAdjustment *vadjustment);
+};
+
+GtkType gtk_text_view_get_type (void);
+GtkWidget * gtk_text_view_new (void);
+GtkWidget * gtk_text_view_new_with_buffer (GtkTextBuffer *buffer);
+void gtk_text_view_set_buffer (GtkTextView *text_view,
+ GtkTextBuffer *buffer);
+GtkTextBuffer *gtk_text_view_get_buffer (GtkTextView *text_view);
+void gtk_text_view_get_iter_at_pixel (GtkTextView *text_view,
+ GtkTextIter *iter,
+ gint x,
+ gint y);
+gboolean gtk_text_view_scroll_to_mark (GtkTextView *text_view,
+ const gchar *mark_name,
+ gint mark_within_margin);
+gboolean gtk_text_view_move_mark_onscreen (GtkTextView *text_view,
+ const gchar *mark_name);
+gboolean gtk_text_view_place_cursor_onscreen (GtkTextView *text_view);
+
+void gtk_text_view_get_visible_rect (GtkTextView *text_view,
+ GdkRectangle *visible_rect);
+void gtk_text_view_set_wrap_mode (GtkTextView *text_view,
+ GtkWrapMode wrap_mode);
+GtkWrapMode gtk_text_view_get_wrap_mode (GtkTextView *text_view);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* GTK_TEXT_VIEW_H */
diff --git a/gtk/gtktooltips.c b/gtk/gtktooltips.c
index f8238a989..1203ce652 100644
--- a/gtk/gtktooltips.c
+++ b/gtk/gtktooltips.c
@@ -28,6 +28,7 @@
#include <string.h>
#include <stdio.h>
+#include "gtklabel.h"
#include "gtkmain.h"
#include "gtkwidget.h"
#include "gtkwindow.h"
@@ -51,8 +52,8 @@ static void gtk_tooltips_widget_remove (GtkWidget *widget,
static void gtk_tooltips_set_active_widget (GtkTooltips *tooltips,
GtkWidget *widget);
static gint gtk_tooltips_timeout (gpointer data);
-static gint gtk_tooltips_paint_window (GtkTooltips *tooltips);
+static gint gtk_tooltips_paint_window (GtkTooltips *tooltips);
static void gtk_tooltips_draw_tips (GtkTooltips *tooltips);
static GtkDataClass *parent_class;
@@ -113,22 +114,10 @@ gtk_tooltips_new (void)
}
static void
-gtk_tooltips_free_string (gpointer data, gpointer user_data)
-{
- if (data)
- g_free (data);
-}
-
-static void
gtk_tooltips_destroy_data (GtkTooltipsData *tooltipsdata)
{
g_free (tooltipsdata->tip_text);
g_free (tooltipsdata->tip_private);
- if (tooltipsdata->row)
- {
- g_list_foreach (tooltipsdata->row, gtk_tooltips_free_string, 0);
- g_list_free (tooltipsdata->row);
- }
gtk_signal_disconnect_by_data (GTK_OBJECT (tooltipsdata->widget),
(gpointer) tooltipsdata);
gtk_object_remove_data (GTK_OBJECT (tooltipsdata->widget), tooltips_data_key);
@@ -178,6 +167,8 @@ gtk_tooltips_force_window (GtkTooltips *tooltips)
gtk_widget_set_app_paintable (tooltips->tip_window, TRUE);
gtk_window_set_policy (GTK_WINDOW (tooltips->tip_window), FALSE, FALSE, TRUE);
gtk_widget_set_name (tooltips->tip_window, "gtk-tooltips");
+ gtk_container_set_border_width (GTK_CONTAINER (tooltips->tip_window), 4);
+
gtk_signal_connect_object (GTK_OBJECT (tooltips->tip_window),
"expose_event",
GTK_SIGNAL_FUNC (gtk_tooltips_paint_window),
@@ -187,6 +178,13 @@ gtk_tooltips_force_window (GtkTooltips *tooltips)
GTK_SIGNAL_FUNC (gtk_tooltips_paint_window),
GTK_OBJECT (tooltips));
+ tooltips->tip_label = gtk_label_new (NULL);
+ gtk_label_set_line_wrap (GTK_LABEL (tooltips->tip_label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (tooltips->tip_label), 0.5, 0.5);
+ gtk_widget_show (tooltips->tip_label);
+
+ gtk_container_add (GTK_CONTAINER (tooltips->tip_window), tooltips->tip_label);
+
gtk_signal_connect (GTK_OBJECT (tooltips->tip_window),
"destroy",
gtk_widget_destroyed,
@@ -194,103 +192,6 @@ gtk_tooltips_force_window (GtkTooltips *tooltips)
}
}
-static void
-gtk_tooltips_layout_text (GtkTooltips *tooltips, GtkTooltipsData *data)
-{
- gchar *row_end, *text, *row_text, *break_pos;
- gint i, row_width, window_width = 0;
- size_t len;
-
- if (!tooltips->tip_window)
- gtk_tooltips_force_window (tooltips);
-
- if (data->row)
- {
- g_list_foreach (data->row, gtk_tooltips_free_string, 0);
- g_list_free (data->row);
- }
- data->row = 0;
- data->font = tooltips->tip_window->style->font;
- data->width = 0;
-
- text = data->tip_text;
- if (!text)
- return;
-
- while (*text)
- {
- row_end = strchr (text, '\n');
- if (!row_end)
- row_end = strchr (text, '\0');
-
- len = row_end - text + 1;
- row_text = g_new(gchar, len);
- memcpy (row_text, text, len - 1);
- row_text[len - 1] = '\0';
-
- /* now either adjust the window's width or shorten the row until
- it fits in the window */
-
- while (1)
- {
- row_width = gdk_string_width (data->font, row_text);
- if (!window_width)
- {
- /* make an initial guess at window's width: */
-
- if (row_width > gdk_screen_width () / 4)
- window_width = gdk_screen_width () / 4;
- else
- window_width = row_width;
- }
- if (row_width <= window_width)
- break;
-
- if (strchr (row_text, ' '))
- {
- /* the row is currently too wide, but we have blanks in
- the row so we can break it into smaller pieces */
-
- gint avg_width = row_width / strlen (row_text);
-
- i = window_width;
- if (avg_width)
- i /= avg_width;
- if ((size_t) i >= len)
- i = len - 1;
-
- break_pos = strchr (row_text + i, ' ');
- if (!break_pos)
- {
- break_pos = row_text + i;
- while (*--break_pos != ' ');
- }
- *break_pos = '\0';
- }
- else
- {
- /* we can't break this row into any smaller pieces, so
- we have no choice but to widen the window: */
-
- window_width = row_width;
- break;
- }
- }
- if (row_width > data->width)
- data->width = row_width;
- data->row = g_list_append (data->row, row_text);
- text += strlen (row_text);
- if (!*text)
- break;
-
- if (text[0] == '\n' && text[1])
- /* end of paragraph and there is more text to come */
- data->row = g_list_append (data->row, 0);
- ++text; /* skip blank or newline */
- }
- data->width += 8; /* leave some border */
-}
-
void
gtk_tooltips_enable (GtkTooltips *tooltips)
{
@@ -356,9 +257,6 @@ gtk_tooltips_set_tip (GtkTooltips *tooltips,
tooltipsdata->tip_text = g_strdup (tip_text);
tooltipsdata->tip_private = g_strdup (tip_private);
- /* Flag data as unitialized */
- tooltipsdata->font = NULL;
-
tooltips->tips_data_list = g_list_append (tooltips->tips_data_list,
tooltipsdata);
gtk_signal_connect_after(GTK_OBJECT (widget), "event",
@@ -396,54 +294,22 @@ gtk_tooltips_set_colors (GtkTooltips *tooltips,
static gint
gtk_tooltips_paint_window (GtkTooltips *tooltips)
{
- GtkStyle *style;
- gint y, baseline_skip, gap;
- GtkTooltipsData *data;
- GList *el;
-
- style = tooltips->tip_window->style;
-
- gap = (style->font->ascent + style->font->descent) / 4;
- if (gap < 2)
- gap = 2;
- baseline_skip = style->font->ascent + style->font->descent + gap;
-
- data = tooltips->active_tips_data;
- if (!data)
- return FALSE;
-
- gtk_paint_flat_box(style, tooltips->tip_window->window,
+ gtk_paint_flat_box(tooltips->tip_window->style, tooltips->tip_window->window,
GTK_STATE_NORMAL, GTK_SHADOW_OUT,
NULL, GTK_WIDGET(tooltips->tip_window), "tooltip",
0, 0, -1, -1);
- y = style->font->ascent + 4;
-
- for (el = data->row; el; el = el->next)
- {
- if (el->data)
- {
- gtk_paint_string (style, tooltips->tip_window->window,
- GTK_STATE_NORMAL,
- NULL, GTK_WIDGET(tooltips->tip_window), "tooltip",
- 4, y, el->data);
- y += baseline_skip;
- }
- else
- y += baseline_skip / 2;
- }
-
- return FALSE;
+ return TRUE;
}
static void
gtk_tooltips_draw_tips (GtkTooltips * tooltips)
{
+ GtkRequisition requisition;
GtkWidget *widget;
GtkStyle *style;
- gint gap, x, y, w, h, scr_w, scr_h, baseline_skip;
+ gint x, y, w, h, scr_w, scr_h;
GtkTooltipsData *data;
- GList *el;
if (!tooltips->tip_window)
gtk_tooltips_force_window (tooltips);
@@ -460,30 +326,18 @@ gtk_tooltips_draw_tips (GtkTooltips * tooltips)
data = tooltips->active_tips_data;
- if (data->font != style->font)
- gtk_tooltips_layout_text (tooltips, data);
-
- gap = (style->font->ascent + style->font->descent) / 4;
- if (gap < 2)
- gap = 2;
- baseline_skip = style->font->ascent + style->font->descent + gap;
+ gtk_label_set_text (GTK_LABEL (tooltips->tip_label), data->tip_text);
- w = data->width;
- h = 8 - gap;
- for (el = data->row; el; el = el->next)
- if (el->data)
- h += baseline_skip;
- else
- h += baseline_skip / 2;
- if (h < 8)
- h = 8;
+ gtk_widget_size_request (tooltips->tip_window, &requisition);
+ w = requisition.width;
+ h = requisition.height;
gdk_window_get_pointer (NULL, &x, NULL, NULL);
gdk_window_get_origin (widget->window, NULL, &y);
if (GTK_WIDGET_NO_WINDOW (widget))
y += widget->allocation.y;
- x -= ((w >> 1) + 4);
+ x -= (w / 2 + 4);
if ((x + w) > scr_w)
x -= (x + w) - scr_w;
@@ -495,7 +349,6 @@ gtk_tooltips_draw_tips (GtkTooltips * tooltips)
else
y = y + widget->allocation.height + 4;
- gtk_widget_set_usize (tooltips->tip_window, w, h);
gtk_widget_popup (tooltips->tip_window, x, y);
}
diff --git a/gtk/gtktooltips.h b/gtk/gtktooltips.h
index bd6c54ae9..7035a60c0 100644
--- a/gtk/gtktooltips.h
+++ b/gtk/gtktooltips.h
@@ -54,9 +54,6 @@ struct _GtkTooltipsData
GtkWidget *widget;
gchar *tip_text;
gchar *tip_private;
- GdkFont *font;
- gint width;
- GList *row;
};
struct _GtkTooltips
@@ -64,6 +61,7 @@ struct _GtkTooltips
GtkData data;
GtkWidget *tip_window;
+ GtkWidget *tip_label;
GtkTooltipsData *active_tips_data;
GList *tips_data_list;
diff --git a/gtk/gtkvruler.c b/gtk/gtkvruler.c
index dec56a3bf..65aa54204 100644
--- a/gtk/gtkvruler.c
+++ b/gtk/gtkvruler.c
@@ -135,7 +135,6 @@ gtk_vruler_draw_ticks (GtkRuler *ruler)
{
GtkWidget *widget;
GdkGC *gc, *bg_gc;
- GdkFont *font;
gint i, j;
gint width, height;
gint xthickness;
@@ -147,10 +146,12 @@ gtk_vruler_draw_ticks (GtkRuler *ruler)
gfloat subd_incr;
gfloat start, end, cur;
gchar unit_str[32];
- gchar digit_str[2] = { '\0', '\0' };
gint digit_height;
+ gint digit_offset;
gint text_height;
gint pos;
+ PangoLayout *layout;
+ PangoRectangle logical_rect, ink_rect;
g_return_if_fail (ruler != NULL);
g_return_if_fail (GTK_IS_VRULER (ruler));
@@ -162,26 +163,31 @@ gtk_vruler_draw_ticks (GtkRuler *ruler)
gc = widget->style->fg_gc[GTK_STATE_NORMAL];
bg_gc = widget->style->bg_gc[GTK_STATE_NORMAL];
- font = widget->style->font;
xthickness = widget->style->klass->xthickness;
ythickness = widget->style->klass->ythickness;
- digit_height = font->ascent; /* assume descent == 0 ? */
+
+ layout = gtk_widget_create_pango_layout (widget);
+ pango_layout_set_text (layout, "012456789", -1);
+ pango_layout_get_extents (layout, &ink_rect, &logical_rect);
+
+ digit_height = ink_rect.height / PANGO_SCALE + 2;
+ digit_offset = ink_rect.y;
width = widget->allocation.height;
height = widget->allocation.width - ythickness * 2;
- gtk_paint_box (widget->style, ruler->backing_store,
- GTK_STATE_NORMAL, GTK_SHADOW_OUT,
- NULL, widget, "vruler",
- 0, 0,
+ gtk_paint_box (widget->style, ruler->backing_store,
+ GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+ NULL, widget, "vruler",
+ 0, 0,
widget->allocation.width, widget->allocation.height);
-
- gdk_draw_line (ruler->backing_store, gc,
+
+ gdk_draw_line (ruler->backing_store, gc,
height + xthickness,
ythickness,
height + xthickness,
widget->allocation.height - ythickness);
-
+
upper = ruler->upper / ruler->metric->pixels_per_unit;
lower = ruler->lower / ruler->metric->pixels_per_unit;
@@ -245,17 +251,22 @@ gtk_vruler_draw_ticks (GtkRuler *ruler)
if (i == 0)
{
sprintf (unit_str, "%d", (int) cur);
+
for (j = 0; j < (int) strlen (unit_str); j++)
{
- digit_str[0] = unit_str[j];
- gdk_draw_string (ruler->backing_store, font, gc,
+ pango_layout_set_text (layout, unit_str + j, 1);
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+
+ gdk_draw_layout (ruler->backing_store, gc,
xthickness + 1,
- pos + digit_height * (j + 1) + 1,
- digit_str);
+ pos + digit_height * j + 2 + (logical_rect.y - digit_offset) / PANGO_SCALE,
+ layout);
}
}
}
}
+
+ pango_layout_unref (layout);
}
diff --git a/gtk/gtkvscale.c b/gtk/gtkvscale.c
index 003760cba..490b383fb 100644
--- a/gtk/gtkvscale.c
+++ b/gtk/gtkvscale.c
@@ -317,14 +317,14 @@ gtk_vscale_size_request (GtkWidget *widget,
GtkRequisition *requisition)
{
GtkScale *scale;
- gint value_width;
+ gint value_width, value_height;
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_VSCALE (widget));
g_return_if_fail (requisition != NULL);
scale = GTK_SCALE (widget);
-
+
requisition->width = (RANGE_CLASS (scale)->slider_width +
widget->style->klass->ythickness * 2);
requisition->height = (SCALE_CLASS (scale)->slider_length +
@@ -332,21 +332,21 @@ gtk_vscale_size_request (GtkWidget *widget,
if (scale->draw_value)
{
- value_width = gtk_scale_get_value_width (scale);
+ gtk_scale_get_value_size (scale, &value_width, &value_height);
if ((scale->value_pos == GTK_POS_LEFT) ||
(scale->value_pos == GTK_POS_RIGHT))
{
requisition->width += value_width + SCALE_CLASS (scale)->value_spacing;
- if (requisition->height < (widget->style->font->ascent + widget->style->font->descent))
- requisition->height = widget->style->font->ascent + widget->style->font->descent;
+ if (requisition->height < (value_height))
+ requisition->height = value_height;
}
else if ((scale->value_pos == GTK_POS_TOP) ||
(scale->value_pos == GTK_POS_BOTTOM))
{
if (requisition->width < value_width)
requisition->width = value_width;
- requisition->height += widget->style->font->ascent + widget->style->font->descent;
+ requisition->height += value_height;
}
}
}
@@ -386,6 +386,7 @@ gtk_vscale_pos_trough (GtkVScale *vscale,
{
GtkWidget *widget;
GtkScale *scale;
+ gint value_width, value_height;
g_return_if_fail (vscale != NULL);
g_return_if_fail (GTK_IS_VSCALE (vscale));
@@ -403,10 +404,12 @@ gtk_vscale_pos_trough (GtkVScale *vscale,
*x = 0;
*y = 0;
+ gtk_scale_get_value_size (scale, &value_width, &value_height);
+
switch (scale->value_pos)
{
case GTK_POS_LEFT:
- *x = (gtk_scale_get_value_width (scale) + SCALE_CLASS (scale)->value_spacing +
+ *x = (value_width + SCALE_CLASS (scale)->value_spacing +
(widget->allocation.width - widget->requisition.width) / 2);
break;
case GTK_POS_RIGHT:
@@ -414,12 +417,12 @@ gtk_vscale_pos_trough (GtkVScale *vscale,
break;
case GTK_POS_TOP:
*x = (widget->allocation.width - *w) / 2;
- *y = widget->style->font->ascent + widget->style->font->descent;
+ *y = value_height;
*h -= *y;
break;
case GTK_POS_BOTTOM:
*x = (widget->allocation.width - *w) / 2;
- *h -= widget->style->font->ascent + widget->style->font->descent;
+ *h -= value_height;
break;
}
}
@@ -512,7 +515,6 @@ gtk_vscale_draw_value (GtkScale *scale)
GtkStateType state_type;
GtkWidget *widget;
gchar buffer[32];
- gint text_width;
gint width, height;
gint x, y;
@@ -523,9 +525,15 @@ gtk_vscale_draw_value (GtkScale *scale)
if (scale->draw_value)
{
- sprintf (buffer, "%0.*f", GTK_RANGE (scale)->digits, GTK_RANGE (scale)->adjustment->value);
- text_width = gdk_string_measure (GTK_WIDGET (scale)->style->font, buffer);
+ PangoLayout *layout;
+ PangoRectangle logical_rect;
+ sprintf (buffer, "%0.*f", GTK_RANGE (scale)->digits, GTK_RANGE (scale)->adjustment->value);
+
+ layout = gtk_widget_create_pango_layout (widget);
+ pango_layout_set_text (layout, buffer, strlen (buffer));
+ pango_layout_get_extents (layout, NULL, &logical_rect);
+
switch (scale->value_pos)
{
case GTK_POS_LEFT:
@@ -534,11 +542,9 @@ gtk_vscale_draw_value (GtkScale *scale)
gdk_window_get_size (GTK_RANGE (scale)->trough, &width, NULL);
gdk_window_get_size (GTK_RANGE (scale)->slider, NULL, &height);
- x -= SCALE_CLASS (scale)->value_spacing + text_width;
- y += widget->allocation.y + ((height -
- (GTK_WIDGET (scale)->style->font->ascent +
- GTK_WIDGET (scale)->style->font->descent)) / 2 +
- GTK_WIDGET (scale)->style->font->ascent);
+ x -= SCALE_CLASS (scale)->value_spacing + logical_rect.width / PANGO_SCALE;
+ y += widget->allocation.y + (height - logical_rect.height / PANGO_SCALE) / 2 +
+ PANGO_ASCENT (logical_rect) / PANGO_SCALE;
break;
case GTK_POS_RIGHT:
gdk_window_get_position (GTK_RANGE (scale)->trough, &x, NULL);
@@ -547,38 +553,44 @@ gtk_vscale_draw_value (GtkScale *scale)
gdk_window_get_size (GTK_RANGE (scale)->slider, NULL, &height);
x += width + SCALE_CLASS (scale)->value_spacing;
- y += widget->allocation.y + ((height -
- (GTK_WIDGET (scale)->style->font->ascent +
- GTK_WIDGET (scale)->style->font->descent)) / 2 +
- GTK_WIDGET (scale)->style->font->ascent);
+ y += widget->allocation.y + (height - logical_rect.height / PANGO_SCALE) / 2 +
+ PANGO_ASCENT (logical_rect) / PANGO_SCALE;
break;
case GTK_POS_TOP:
gdk_window_get_position (GTK_RANGE (scale)->trough, &x, &y);
gdk_window_get_size (GTK_RANGE (scale)->slider, &width, NULL);
gdk_window_get_size (GTK_RANGE (scale)->trough, NULL, &height);
- x += (width - text_width) / 2;
- y -= GTK_WIDGET (scale)->style->font->descent;
+ x += (width - logical_rect.width / PANGO_SCALE) / 2;
+ y -= PANGO_DESCENT (logical_rect) / PANGO_SCALE;
break;
case GTK_POS_BOTTOM:
gdk_window_get_position (GTK_RANGE (scale)->trough, &x, &y);
gdk_window_get_size (GTK_RANGE (scale)->slider, &width, NULL);
gdk_window_get_size (GTK_RANGE (scale)->trough, NULL, &height);
- x += (width - text_width) / 2;
- y += height + GTK_WIDGET (scale)->style->font->ascent;
+ x += (width - logical_rect.width / PANGO_SCALE) / 2;
+ y += height + PANGO_ASCENT (logical_rect) / PANGO_SCALE;
break;
}
state_type = GTK_STATE_NORMAL;
if (!GTK_WIDGET_IS_SENSITIVE (scale))
state_type = GTK_STATE_INSENSITIVE;
-
+
+#if 0
gtk_paint_string (GTK_WIDGET (scale)->style,
GTK_WIDGET (scale)->window,
state_type,
NULL, GTK_WIDGET (scale), "vscale",
x, y, buffer);
+#endif
+
+ gdk_draw_layout (GTK_WIDGET (scale)->window,
+ GTK_WIDGET (scale)->style->fg_gc [state_type],
+ x, y, layout);
+
+ pango_layout_unref (layout);
}
}
diff --git a/gtk/gtkvscrollbar.c b/gtk/gtkvscrollbar.c
index 833f003a6..af4824353 100644
--- a/gtk/gtkvscrollbar.c
+++ b/gtk/gtkvscrollbar.c
@@ -418,7 +418,10 @@ gtk_vscrollbar_calc_slider_size (GtkVScrollbar *vscrollbar)
gdk_window_get_size (range->slider, &slider_width, &slider_height);
if (slider_height != height)
- gdk_window_resize (range->slider, slider_width, height);
+ {
+ gdk_window_resize (range->slider, slider_width, height);
+ gdk_window_invalidate_rect (range->slider, NULL, FALSE);
+ }
}
}
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index e30ca4aa6..dcb98970c 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -26,6 +26,7 @@
#include <stdarg.h>
#include <string.h>
+#include <locale.h>
#include "gtkcontainer.h"
#include "gtkmain.h"
#include "gtkrc.h"
@@ -178,8 +179,6 @@ static void gtk_widget_set_style_internal (GtkWidget *widget,
static void gtk_widget_set_style_recurse (GtkWidget *widget,
gpointer client_data);
-static gboolean gtk_widget_is_offscreen (GtkWidget *widget);
-
static GtkWidgetAuxInfo* gtk_widget_aux_info_new (void);
static void gtk_widget_aux_info_destroy (GtkWidgetAuxInfo *aux_info);
@@ -214,6 +213,8 @@ static const gchar *visual_key = "gtk-visual";
static const gchar *rc_style_key = "gtk-rc-style";
static guint rc_style_key_id = 0;
+static GtkTextDirection gtk_default_direction = GTK_TEXT_DIR_LTR;
+
/*****************************************
* gtk_widget_get_type:
*
@@ -1229,13 +1230,8 @@ gtk_widget_queue_clear_child (GtkWidget *widget)
{
GtkWidget *parent;
- /* We check for GTK_WIDGET_IS_OFFSCREEN (widget),
- * and queue_clear_area(parent...) will check the rest of
- * way up the tree with gtk_widget_is_offscreen (parent)
- */
parent = widget->parent;
- if (parent && GTK_WIDGET_DRAWABLE (parent) &&
- !GTK_WIDGET_IS_OFFSCREEN (widget))
+ if (parent && GTK_WIDGET_DRAWABLE (parent))
gtk_widget_queue_clear_area (parent,
widget->allocation.x,
widget->allocation.y,
@@ -1743,8 +1739,7 @@ gtk_widget_queue_clear_area (GtkWidget *widget,
g_return_if_fail (widget != NULL);
g_return_if_fail (GTK_IS_WIDGET (widget));
- if (!(widget->window && gdk_window_is_viewable (widget->window)) ||
- gtk_widget_is_offscreen (widget))
+ if (!(widget->window && gdk_window_is_viewable (widget->window)))
return;
/* Find the correct widget */
@@ -3181,6 +3176,71 @@ gtk_widget_pop_style (void)
}
}
+/**
+ * gtk_widget_create_pango_context:
+ * @widget: a #PangoWidget
+ *
+ * Create a new pango context with the appropriate colormap,
+ * font description, and base direction for drawing text for
+ * this widget.
+ *
+ * Return value: the new #PangoContext
+ **/
+PangoContext *
+gtk_widget_create_pango_context (GtkWidget *widget)
+{
+ PangoContext *context;
+ char *lang, *p;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ context = gdk_pango_context_get ();
+
+ gdk_pango_context_set_colormap (context, gtk_widget_get_colormap (widget));
+ pango_context_set_base_dir (context,
+ gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR ?
+ PANGO_DIRECTION_LTR : PANGO_DIRECTION_RTL);
+ pango_context_set_font_description (context, widget->style->font_desc);
+
+ lang = g_strdup (setlocale (LC_CTYPE, NULL));
+ p = strchr (lang, '.');
+ if (p)
+ *p = '\0';
+ p = strchr (lang, '@');
+ if (p)
+ *p = '\0';
+
+ pango_context_set_lang (context, lang);
+ g_free (lang);
+
+ return context;
+}
+
+/**
+ * gtk_widget_create_pango_layout:
+ * @widget: a #PangoWidget
+ *
+ * Create a new #PangoLayout with the appropriate colormap,
+ * font description, and base direction for drawing text for
+ * this widget.
+ *
+ * Return value: the new #PangoLayout
+ **/
+PangoLayout *
+gtk_widget_create_pango_layout (GtkWidget *widget)
+{
+ PangoLayout *layout;
+ PangoContext *context;
+
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ context = gtk_widget_create_pango_context (widget);
+ layout = pango_layout_new (context);
+ pango_context_unref (context);
+
+ return layout;
+}
+
/*************************************************************
* gtk_widget_set_parent_window:
* Set a non default parent window for widget
@@ -3887,6 +3947,93 @@ gtk_widget_get_default_visual (void)
return default_visual;
}
+/**
+ * gtk_widget_set_direction:
+ * @widget: a #GtkWidget
+ * @dir: the new direction
+ *
+ * Set the reading direction on a particular widget. This direction
+ * controls the primary direction for widgets containing text,
+ * and also the direction in which the children of a container are
+ * packed. The ability to set the direction is present in order
+ * so that correct localization into languages with right-to-left
+ * reading directions can be done. Generally, applications will
+ * let the default reading direction present, except for containers
+ * where the containers are arranged in an order that is explicitely
+ * visual rather than logical (such as buttons for text justificiation).
+ *
+ * If the direction is set to %GTK_TEXT_DIR_NONE, then the value
+ * set by gtk_widget_set_default_direction() will be used.
+ **/
+void
+gtk_widget_set_direction (GtkWidget *widget,
+ GtkTextDirection dir)
+{
+ g_return_if_fail (widget != NULL);
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+ g_return_if_fail (dir >= GTK_TEXT_DIR_NONE && dir <= GTK_TEXT_DIR_RTL);
+
+ if (dir == GTK_TEXT_DIR_NONE)
+ GTK_PRIVATE_UNSET_FLAG (widget, GTK_DIRECTION_SET);
+ else
+ {
+ GTK_PRIVATE_SET_FLAG (widget, GTK_DIRECTION_SET);
+ if (dir == GTK_TEXT_DIR_LTR)
+ GTK_PRIVATE_SET_FLAG (widget, GTK_DIRECTION_LTR);
+ else
+ GTK_PRIVATE_UNSET_FLAG (widget, GTK_DIRECTION_LTR);
+ }
+}
+
+/**
+ * gtk_widget_get_direction:
+ * @widget: a #GtkWidget
+ *
+ * Get the reading direction for a particular widget. See
+ * gtk_widget_set_direction().
+ *
+ * Return value: the reading direction for the widget.
+ **/
+GtkTextDirection
+gtk_widget_get_direction (GtkWidget *widget)
+{
+ g_return_val_if_fail (widget != NULL, GTK_TEXT_DIR_LTR);
+ g_return_if_fail (GTK_IS_WIDGET (widget));
+
+ if (GTK_WIDGET_DIRECTION_SET (widget))
+ return GTK_WIDGET_DIRECTION_LTR (widget) ? GTK_TEXT_DIR_LTR : GTK_TEXT_DIR_RTL;
+ else
+ return gtk_default_direction;
+}
+
+/**
+ * gtk_widget_set_default_direction:
+ * @dir: the new default direction. This cannot be
+ * %GTK_TEXT_DIR_NONE.
+ *
+ * Set the default reading direction for widgets where the
+ * direction has not been explicitely set by gtk_widget_set_direction().
+ **/
+void
+gtk_widget_set_default_direction (GtkTextDirection dir)
+{
+ g_return_if_fail (dir == GTK_TEXT_DIR_RTL || GTK_TEXT_DIR_LTR);
+
+ gtk_default_direction = dir;
+}
+
+/**
+ * gtk_widget_get_default_direction:
+ *
+ * Return value: the current default direction. See
+ * gtk_widget_set_direction().
+ **/
+GtkTextDirection
+gtk_widget_get_default_direction (void)
+{
+ return gtk_default_direction;
+}
+
static void
gtk_widget_shutdown (GObject *object)
{
@@ -4230,29 +4377,6 @@ gtk_widget_propagate_state (GtkWidget *widget,
}
}
-/*************************************************************
- * gtk_widget_is_offscreen:
- * Check if a widget is "offscreen"
- * arguments:
- * widget: a widget
- * results:
- * TRUE if the widget or any of ancestors has the
- * PRIVATE_GTK_WIDGET_IS_OFFSCREEN set.
- *************************************************************/
-
-static gboolean
-gtk_widget_is_offscreen (GtkWidget *widget)
-{
- while (widget)
- {
- if (GTK_WIDGET_IS_OFFSCREEN (widget))
- return TRUE;
- widget = widget->parent;
- }
-
- return FALSE;
-}
-
/*****************************************
* gtk_widget_aux_info_new:
*
diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h
index 2b2579ddb..3d804a137 100644
--- a/gtk/gtkwidget.h
+++ b/gtk/gtkwidget.h
@@ -572,6 +572,9 @@ void gtk_widget_restore_default_style (GtkWidget *widget);
void gtk_widget_modify_style (GtkWidget *widget,
GtkRcStyle *style);
+PangoContext *gtk_widget_create_pango_context (GtkWidget *widget);
+PangoLayout *gtk_widget_create_pango_layout (GtkWidget *widget);
+
/* handle composite names for GTK_COMPOSITE_CHILD widgets,
* the returned name is newly allocated.
*/
@@ -604,6 +607,16 @@ GtkStyle* gtk_widget_get_default_style (void);
GdkColormap* gtk_widget_get_default_colormap (void);
GdkVisual* gtk_widget_get_default_visual (void);
+/* Functions for setting directionality for widgets
+ */
+
+void gtk_widget_set_direction (GtkWidget *widget,
+ GtkTextDirection dir);
+GtkTextDirection gtk_widget_get_direction (GtkWidget *widget);
+
+void gtk_widget_set_default_direction (GtkTextDirection dir);
+GtkTextDirection gtk_widget_get_default_direction (void);
+
/* Counterpart to gdk_window_shape_combine_mask.
*/
void gtk_widget_shape_combine_mask (GtkWidget *widget,
diff --git a/gtk/testcalendar.c b/gtk/testcalendar.c
new file mode 100644
index 000000000..0eaa0c960
--- /dev/null
+++ b/gtk/testcalendar.c
@@ -0,0 +1,422 @@
+/* example-start calendar calendar.c */
+/*
+ * Copyright (C) 1998 Cesar Miquel, Shawn T. Amundson, Mattias Grnlund
+ * Copyright (C) 2000 Tony Gale
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <gtk/gtk.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+
+#define DEF_PAD 10
+#define DEF_PAD_SMALL 5
+
+#define TM_YEAR_BASE 1900
+
+typedef struct _CalendarData {
+ GtkWidget *flag_checkboxes[5];
+ gboolean settings[5];
+ GtkWidget *font_dialog;
+ GtkWidget *window;
+ GtkWidget *prev2_sig;
+ GtkWidget *prev_sig;
+ GtkWidget *last_sig;
+ GtkWidget *month;
+} CalendarData;
+
+enum {
+ calendar_show_header,
+ calendar_show_days,
+ calendar_month_change,
+ calendar_show_week,
+ calendar_monday_first
+};
+
+/*
+ * GtkCalendar
+ */
+
+void calendar_date_to_string( CalendarData *data,
+ char *buffer,
+ gint buff_len )
+{
+ struct tm tm;
+ time_t time;
+
+ memset (&tm, 0, sizeof (tm));
+ gtk_calendar_get_date (GTK_CALENDAR(data->window),
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday);
+ tm.tm_year -= TM_YEAR_BASE;
+ time = mktime(&tm);
+ strftime (buffer, buff_len-1, "%x", gmtime(&time));
+}
+
+void calendar_set_signal_strings (char *sig_str,
+ CalendarData *data)
+{
+ gchar *prev_sig;
+
+ gtk_label_get (GTK_LABEL (data->prev_sig), &prev_sig);
+ gtk_label_set_text (GTK_LABEL (data->prev2_sig), prev_sig);
+
+ gtk_label_get (GTK_LABEL (data->last_sig), &prev_sig);
+ gtk_label_set_text (GTK_LABEL (data->prev_sig), prev_sig);
+ gtk_label_set_text (GTK_LABEL (data->last_sig), sig_str);
+}
+
+void calendar_month_changed( GtkWidget *widget,
+ CalendarData *data )
+{
+ char buffer[256] = "month_changed: ";
+
+ calendar_date_to_string (data, buffer+15, 256-15);
+ calendar_set_signal_strings (buffer, data);
+}
+
+void calendar_day_selected( GtkWidget *widget,
+ CalendarData *data )
+{
+ char buffer[256] = "day_selected: ";
+
+ calendar_date_to_string (data, buffer+14, 256-14);
+ calendar_set_signal_strings (buffer, data);
+}
+
+void calendar_day_selected_double_click( GtkWidget *widget,
+ CalendarData *data )
+{
+ struct tm tm;
+ char buffer[256] = "day_selected_double_click: ";
+
+ calendar_date_to_string (data, buffer+27, 256-27);
+ calendar_set_signal_strings (buffer, data);
+
+ memset (&tm, 0, sizeof (tm));
+ gtk_calendar_get_date (GTK_CALENDAR(data->window),
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday);
+ tm.tm_year -= TM_YEAR_BASE;
+
+ if(GTK_CALENDAR(data->window)->marked_date[tm.tm_mday-1] == 0) {
+ gtk_calendar_mark_day(GTK_CALENDAR(data->window),tm.tm_mday);
+ } else {
+ gtk_calendar_unmark_day(GTK_CALENDAR(data->window),tm.tm_mday);
+ }
+}
+
+void calendar_prev_month( GtkWidget *widget,
+ CalendarData *data )
+{
+ char buffer[256] = "prev_month: ";
+
+ calendar_date_to_string (data, buffer+12, 256-12);
+ calendar_set_signal_strings (buffer, data);
+}
+
+void calendar_next_month( GtkWidget *widget,
+ CalendarData *data )
+{
+ char buffer[256] = "next_month: ";
+
+ calendar_date_to_string (data, buffer+12, 256-12);
+ calendar_set_signal_strings (buffer, data);
+}
+
+void calendar_prev_year( GtkWidget *widget,
+ CalendarData *data )
+{
+ char buffer[256] = "prev_year: ";
+
+ calendar_date_to_string (data, buffer+11, 256-11);
+ calendar_set_signal_strings (buffer, data);
+}
+
+void calendar_next_year( GtkWidget *widget,
+ CalendarData *data )
+{
+ char buffer[256] = "next_year: ";
+
+ calendar_date_to_string (data, buffer+11, 256-11);
+ calendar_set_signal_strings (buffer, data);
+}
+
+
+void calendar_set_flags( CalendarData *calendar )
+{
+ gint i;
+ gint options=0;
+ for (i=0;i<5;i++)
+ if (calendar->settings[i])
+ {
+ options=options + (1<<i);
+ }
+ if (calendar->window)
+ gtk_calendar_display_options (GTK_CALENDAR (calendar->window), options);
+}
+
+void calendar_toggle_flag( GtkWidget *toggle,
+ CalendarData *calendar )
+{
+ gint i;
+ gint j;
+ j=0;
+ for (i=0; i<5; i++)
+ if (calendar->flag_checkboxes[i] == toggle)
+ j = i;
+
+ calendar->settings[j]=!calendar->settings[j];
+ calendar_set_flags(calendar);
+
+}
+
+void calendar_font_selection_ok (GtkWidget *button,
+ CalendarData *calendar)
+{
+ GtkRcStyle *style;
+ char *font_name;
+
+ if (calendar->window)
+ {
+ font_name = gtk_font_selection_dialog_get_font_name (GTK_FONT_SELECTION_DIALOG(calendar->font_dialog));
+ if (font_name)
+ {
+ style = gtk_rc_style_new ();
+ pango_font_description_free (style->font_desc);
+ style->font_desc = pango_font_description_from_string (font_name);
+ gtk_widget_modify_style (calendar->window, style);
+ g_free (font_name);
+ }
+ }
+
+ gtk_widget_destroy (calendar->font_dialog);
+}
+
+void calendar_select_font( GtkWidget *button,
+ CalendarData *calendar )
+{
+ GtkWidget *window;
+
+ if (!calendar->font_dialog) {
+ window = gtk_font_selection_dialog_new ("Font Selection Dialog");
+ g_return_if_fail(GTK_IS_FONT_SELECTION_DIALOG(window));
+ calendar->font_dialog = window;
+
+ gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_MOUSE);
+
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC (gtk_widget_destroyed),
+ &calendar->font_dialog);
+
+ gtk_signal_connect (GTK_OBJECT (GTK_FONT_SELECTION_DIALOG (window)->ok_button),
+ "clicked", GTK_SIGNAL_FUNC(calendar_font_selection_ok),
+ calendar);
+ gtk_signal_connect_object (GTK_OBJECT (GTK_FONT_SELECTION_DIALOG (window)->cancel_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy),
+ GTK_OBJECT (calendar->font_dialog));
+ }
+ window=calendar->font_dialog;
+ if (!GTK_WIDGET_VISIBLE (window))
+ gtk_widget_show (window);
+ else
+ gtk_widget_destroy (window);
+
+}
+
+void create_calendar()
+{
+ GtkWidget *window;
+ GtkWidget *vbox, *vbox2, *vbox3;
+ GtkWidget *hbox;
+ GtkWidget *hbbox;
+ GtkWidget *calendar;
+ GtkWidget *toggle;
+ GtkWidget *button;
+ GtkWidget *frame;
+ GtkWidget *separator;
+ GtkWidget *label;
+ GtkWidget *bbox;
+ static CalendarData calendar_data;
+ gint i;
+
+ struct {
+ char *label;
+ } flags[] =
+ {
+ { "Show Heading" },
+ { "Show Day Names" },
+ { "No Month Change" },
+ { "Show Week Numbers" },
+ { "Week Start Monday" }
+ };
+
+
+ calendar_data.window = NULL;
+ calendar_data.font_dialog = NULL;
+
+ for (i=0; i<5; i++) {
+ calendar_data.settings[i]=0;
+ }
+
+ window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_title(GTK_WINDOW(window), "GtkCalendar Example");
+ gtk_container_set_border_width (GTK_CONTAINER (window), 5);
+ gtk_signal_connect(GTK_OBJECT(window), "destroy",
+ GTK_SIGNAL_FUNC(gtk_main_quit),
+ NULL);
+ gtk_signal_connect(GTK_OBJECT(window), "delete-event",
+ GTK_SIGNAL_FUNC(gtk_false),
+ NULL);
+
+ gtk_window_set_policy(GTK_WINDOW(window), FALSE, FALSE, TRUE);
+
+ vbox = gtk_vbox_new(FALSE, DEF_PAD);
+ gtk_container_add (GTK_CONTAINER (window), vbox);
+
+ /*
+ * The top part of the window, Calendar, flags and fontsel.
+ */
+
+ hbox = gtk_hbox_new(FALSE, DEF_PAD);
+ gtk_box_pack_start (GTK_BOX(vbox), hbox, TRUE, TRUE, DEF_PAD);
+ hbbox = gtk_hbutton_box_new();
+ gtk_box_pack_start(GTK_BOX(hbox), hbbox, FALSE, FALSE, DEF_PAD);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(hbbox), GTK_BUTTONBOX_SPREAD);
+ gtk_button_box_set_spacing(GTK_BUTTON_BOX(hbbox), 5);
+
+ /* Calendar widget */
+ frame = gtk_frame_new("Calendar");
+ gtk_box_pack_start(GTK_BOX(hbbox), frame, FALSE, TRUE, DEF_PAD);
+ calendar=gtk_calendar_new();
+ calendar_data.window = calendar;
+ calendar_set_flags(&calendar_data);
+ gtk_calendar_mark_day ( GTK_CALENDAR(calendar), 19);
+ gtk_container_add( GTK_CONTAINER( frame), calendar);
+ gtk_signal_connect (GTK_OBJECT (calendar), "month_changed",
+ GTK_SIGNAL_FUNC (calendar_month_changed),
+ &calendar_data);
+ gtk_signal_connect (GTK_OBJECT (calendar), "day_selected",
+ GTK_SIGNAL_FUNC (calendar_day_selected),
+ &calendar_data);
+ gtk_signal_connect (GTK_OBJECT (calendar), "day_selected_double_click",
+ GTK_SIGNAL_FUNC (calendar_day_selected_double_click),
+ &calendar_data);
+ gtk_signal_connect (GTK_OBJECT (calendar), "prev_month",
+ GTK_SIGNAL_FUNC (calendar_prev_month),
+ &calendar_data);
+ gtk_signal_connect (GTK_OBJECT (calendar), "next_month",
+ GTK_SIGNAL_FUNC (calendar_next_month),
+ &calendar_data);
+ gtk_signal_connect (GTK_OBJECT (calendar), "prev_year",
+ GTK_SIGNAL_FUNC (calendar_prev_year),
+ &calendar_data);
+ gtk_signal_connect (GTK_OBJECT (calendar), "next_year",
+ GTK_SIGNAL_FUNC (calendar_next_year),
+ &calendar_data);
+
+
+ separator = gtk_vseparator_new ();
+ gtk_box_pack_start (GTK_BOX (hbox), separator, FALSE, TRUE, 0);
+
+ vbox2 = gtk_vbox_new(FALSE, DEF_PAD);
+ gtk_box_pack_start(GTK_BOX(hbox), vbox2, FALSE, FALSE, DEF_PAD);
+
+ /* Build the Right frame with the flags in */
+
+ frame = gtk_frame_new("Flags");
+ gtk_box_pack_start(GTK_BOX(vbox2), frame, TRUE, TRUE, DEF_PAD);
+ vbox3 = gtk_vbox_new(TRUE, DEF_PAD_SMALL);
+ gtk_container_add(GTK_CONTAINER(frame), vbox3);
+
+ for (i = 0; i < 5; i++)
+ {
+ toggle = gtk_check_button_new_with_label(flags[i].label);
+ gtk_signal_connect (GTK_OBJECT (toggle),
+ "toggled",
+ GTK_SIGNAL_FUNC(calendar_toggle_flag),
+ &calendar_data);
+ gtk_box_pack_start (GTK_BOX (vbox3), toggle, TRUE, TRUE, 0);
+ calendar_data.flag_checkboxes[i]=toggle;
+ }
+ /* Build the right font-button */
+ button = gtk_button_new_with_label("Font...");
+ gtk_signal_connect (GTK_OBJECT (button),
+ "clicked",
+ GTK_SIGNAL_FUNC(calendar_select_font),
+ &calendar_data);
+ gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
+
+ /*
+ * Build the Signal-event part.
+ */
+
+ frame = gtk_frame_new("Signal events");
+ gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, DEF_PAD);
+
+ vbox2 = gtk_vbox_new(TRUE, DEF_PAD_SMALL);
+ gtk_container_add(GTK_CONTAINER(frame), vbox2);
+
+ hbox = gtk_hbox_new (FALSE, 3);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, TRUE, 0);
+ label = gtk_label_new ("Signal:");
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
+ calendar_data.last_sig = gtk_label_new ("");
+ gtk_box_pack_start (GTK_BOX (hbox), calendar_data.last_sig, FALSE, TRUE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 3);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, TRUE, 0);
+ label = gtk_label_new ("Previous signal:");
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
+ calendar_data.prev_sig = gtk_label_new ("");
+ gtk_box_pack_start (GTK_BOX (hbox), calendar_data.prev_sig, FALSE, TRUE, 0);
+
+ hbox = gtk_hbox_new (FALSE, 3);
+ gtk_box_pack_start (GTK_BOX (vbox2), hbox, FALSE, TRUE, 0);
+ label = gtk_label_new ("Second previous signal:");
+ gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0);
+ calendar_data.prev2_sig = gtk_label_new ("");
+ gtk_box_pack_start (GTK_BOX (hbox), calendar_data.prev2_sig, FALSE, TRUE, 0);
+
+ bbox = gtk_hbutton_box_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), bbox, FALSE, FALSE, 0);
+ gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
+
+ button = gtk_button_new_with_label ("Close");
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (gtk_main_quit),
+ NULL);
+ gtk_container_add (GTK_CONTAINER (bbox), button);
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_widget_grab_default (button);
+
+ gtk_widget_show_all(window);
+}
+
+
+int main(int argc,
+ char *argv[] )
+{
+ gtk_set_locale ();
+ gtk_init (&argc, &argv);
+
+ create_calendar();
+
+ gtk_main();
+
+ return(0);
+}
+/* example-end */
diff --git a/gtk/testgtk.c b/gtk/testgtk.c
index bee099610..9df3f2e0c 100644
--- a/gtk/testgtk.c
+++ b/gtk/testgtk.c
@@ -43,6 +43,7 @@
#include "gdk/gdkkeysyms.h"
#include "circles.xbm"
+#include "test.xpm"
typedef struct _OptionMenuItem
{
@@ -50,6 +51,14 @@ typedef struct _OptionMenuItem
GtkSignalFunc func;
} OptionMenuItem;
+gboolean
+file_exists (const char *filename)
+{
+ struct stat statbuf;
+
+ return stat (filename, &statbuf) == 0;
+}
+
GtkWidget *
shape_create_icon (char *xpm_file,
gint x,
@@ -586,9 +595,18 @@ new_pixmap (char *filename,
GdkPixmap *pixmap;
GdkBitmap *mask;
- pixmap = gdk_pixmap_create_from_xpm (window, &mask,
- background,
- filename);
+ if (strcmp (filename, "test.xpm") == 0 ||
+ !file_exists (filename))
+ {
+ pixmap = gdk_pixmap_create_from_xpm_d (window, &mask,
+ background,
+ openfile);
+ }
+ else
+ pixmap = gdk_pixmap_create_from_xpm (window, &mask,
+ background,
+ filename);
+
wpixmap = gtk_pixmap_new (pixmap, mask);
return wpixmap;
@@ -1696,6 +1714,22 @@ void create_labels (void)
gtk_container_add (GTK_CONTAINER (frame), label);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+ frame = gtk_frame_new ("Internationalized Label");
+ label = gtk_label_new ("French (Français) Bonjour, Salut\n"
+ "Korean (한글) 안녕하세요, 안녕하십니까\n"
+ "Russian (Русский) Здравствуйте!");
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
+ gtk_container_add (GTK_CONTAINER (frame), label);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
+ frame = gtk_frame_new ("Bidirection Label");
+ label = gtk_label_new ("Arabic السلام عليكم\n"
+ "Hebrew שלום");
+ gtk_widget_set_direction (label, GTK_TEXT_DIR_RTL);
+ gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_RIGHT);
+ gtk_container_add (GTK_CONTAINER (frame), label);
+ gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
+
vbox = gtk_vbox_new (FALSE, 5);
gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 0);
frame = gtk_frame_new ("Line wrapped label");
@@ -1726,12 +1760,11 @@ void create_labels (void)
frame = gtk_frame_new ("Underlined label");
label = gtk_label_new ("This label is underlined!\n"
- "This one is underlined in ܸquite a funky fashion");
+ "This one is underlined (こんにちは) in quite a funky fashion");
gtk_label_set_justify (GTK_LABEL (label), GTK_JUSTIFY_LEFT);
gtk_label_set_pattern (GTK_LABEL (label), "_________________________ _ _________ _ _____ _ __ __ ___ ____ _____");
gtk_container_add (GTK_CONTAINER (frame), label);
gtk_box_pack_start (GTK_BOX (vbox), frame, FALSE, FALSE, 0);
-
}
if (!GTK_WIDGET_VISIBLE (window))
gtk_widget_show_all (window);
@@ -1983,8 +2016,6 @@ create_pixmap (void)
GtkWidget *label;
GtkWidget *separator;
GtkWidget *pixmapwid;
- GdkPixmap *pixmap;
- GdkBitmap *mask;
if (!window)
{
@@ -2008,11 +2039,7 @@ create_pixmap (void)
button = gtk_button_new ();
gtk_box_pack_start (GTK_BOX (box2), button, FALSE, FALSE, 0);
- pixmap = gdk_pixmap_create_from_xpm (window->window, &mask, NULL,
- "test.xpm");
- pixmapwid = gtk_pixmap_new (pixmap, mask);
- gdk_pixmap_unref (pixmap);
- gdk_pixmap_unref (mask);
+ pixmapwid = new_pixmap ("test.xpm", window->window, NULL);
label = gtk_label_new ("Pixmap\ntest");
box3 = gtk_hbox_new (FALSE, 0);
@@ -2830,7 +2857,7 @@ create_entry (void)
gtk_widget_show (box2);
entry = gtk_entry_new ();
- gtk_entry_set_text (GTK_ENTRY (entry), "hello world");
+ gtk_entry_set_text (GTK_ENTRY (entry), "hello world السلام عليكم");
gtk_editable_select_region (GTK_EDITABLE (entry), 0, 5);
gtk_box_pack_start (GTK_BOX (box2), entry, TRUE, TRUE, 0);
gtk_widget_show (entry);
@@ -3946,9 +3973,8 @@ insert_row_clist (GtkWidget *widget, gpointer data)
style3 = gtk_style_copy (GTK_WIDGET (data)->style);
style3->fg[GTK_STATE_NORMAL] = col1;
style3->base[GTK_STATE_NORMAL] = col2;
- gdk_font_unref (style3->font);
- style3->font =
- gdk_font_load ("-*-courier-medium-*-*-*-*-120-*-*-*-*-*-*");
+ pango_font_description_free (style3->font_desc);
+ style3->font_desc = pango_font_description_from_string ("courier 12");
}
gtk_clist_set_cell_style (GTK_CLIST (data), row, 3, style1);
@@ -4197,10 +4223,9 @@ create_clist (void)
style = gtk_style_new ();
style->fg[GTK_STATE_NORMAL] = col1;
style->base[GTK_STATE_NORMAL] = col2;
-
- gdk_font_unref (style->font);
- style->font =
- gdk_font_load ("-adobe-helvetica-bold-r-*-*-*-140-*-*-*-*-*-*");
+
+ style->font_desc->size = 14 * PANGO_SCALE;
+ style->font_desc->weight = PANGO_WEIGHT_BOLD;
for (i = 0; i < 10; i++)
{
@@ -4369,9 +4394,8 @@ void change_style (GtkWidget *widget, GtkCTree *ctree)
style2->base[GTK_STATE_SELECTED] = col2;
style2->fg[GTK_STATE_NORMAL] = col1;
style2->base[GTK_STATE_NORMAL] = col2;
- gdk_font_unref (style2->font);
- style2->font =
- gdk_font_load ("-*-courier-medium-*-*-*-*-300-*-*-*-*-*-*");
+ pango_font_description_free (style2->font_desc);
+ style2->font_desc = pango_font_description_from_string ("courier 30");
}
gtk_ctree_node_set_cell_style (ctree, node, 1, style1);
@@ -5330,6 +5354,72 @@ create_file_selection (void)
gtk_widget_destroy (window);
}
+void
+flipping_toggled_cb (GtkWidget *widget, gpointer data)
+{
+ int state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget));
+ int new_direction = state ? GTK_TEXT_DIR_RTL : GTK_TEXT_DIR_LTR;
+
+ if (new_direction != gtk_widget_get_default_direction ())
+ {
+ GList *toplevels;
+
+ gtk_widget_set_default_direction (new_direction);
+
+ toplevels = gtk_window_list_toplevels ();
+ while (toplevels)
+ {
+ gtk_widget_queue_resize (toplevels->data);
+ g_object_unref (toplevels->data);
+ toplevels = toplevels->next;
+ }
+
+ g_list_free (toplevels);
+ }
+
+}
+
+void
+create_flipping (void)
+{
+ static GtkWidget *window = NULL;
+ GtkWidget *check_button, *button;
+
+ if (!window)
+ {
+ window = gtk_dialog_new ();
+
+ gtk_signal_connect (GTK_OBJECT (window), "destroy",
+ GTK_SIGNAL_FUNC(gtk_widget_destroyed),
+ &window);
+
+ gtk_window_set_title (GTK_WINDOW (window), "Bidirectional Flipping");
+
+ check_button = gtk_check_button_new_with_label ("Right-to-left global direction");
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->vbox),
+ check_button, TRUE, TRUE, 0);
+
+ if (gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL)
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button), TRUE);
+
+ gtk_signal_connect (GTK_OBJECT (check_button), "toggled",
+ flipping_toggled_cb, FALSE);
+
+ gtk_container_set_border_width (GTK_CONTAINER (check_button), 10);
+
+ button = gtk_button_new_with_label ("Close");
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (window));
+ gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area),
+ button, TRUE, TRUE, 0);
+ }
+
+ if (!GTK_WIDGET_VISIBLE (window))
+ gtk_widget_show_all (window);
+ else
+ gtk_widget_destroy (window);
+}
+
/*
* GtkFontSelection
*/
@@ -6778,6 +6868,12 @@ create_shapes (void)
static GtkWidget *sheets = NULL;
static GtkWidget *rings = NULL;
+ if (!(file_exists ("Modeller.xpm") &&
+ file_exists ("FilesQueue.xpm") &&
+ file_exists ("3DRings.xpm")))
+ return;
+
+
if (!modeller)
{
modeller = shape_create_icon ("Modeller.xpm",
@@ -8434,6 +8530,7 @@ create_main_window (void)
{ "entry", create_entry },
{ "event watcher", create_event_watcher },
{ "file selection", create_file_selection },
+ { "flipping", create_flipping },
{ "font selection", create_font_selection },
{ "gamma curve", create_gamma_curve },
{ "handle box", create_handle_box },
@@ -8562,7 +8659,6 @@ int
main (int argc, char *argv[])
{
GtkBindingSet *binding_set;
- struct stat statbuf;
srand (time (NULL));
@@ -8571,14 +8667,8 @@ main (int argc, char *argv[])
/* Check to see if we are being run from the correct
* directory.
*/
- if (stat("./testgtkrc", &statbuf) < 0)
- {
- fprintf (stderr, "*** The testgtk program must be run from within the\n"
- "*** gtk/ subdirectory of the GTK+ distribution.\n");
- exit (1);
- }
-
- gtk_rc_add_default_file ("testgtkrc");
+ if (file_exists ("testgtkrc"))
+ gtk_rc_add_default_file ("testgtkrc");
gtk_init (&argc, &argv);
diff --git a/gtk/testgtkrc b/gtk/testgtkrc
index abc738257..7f3978656 100644
--- a/gtk/testgtkrc
+++ b/gtk/testgtkrc
@@ -26,8 +26,7 @@ pixmap_path "."
style "defaultfont"
{
-# fontset = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*,*"
- font = "-adobe-helvetica-medium-r-normal--*-120-*-*-*-*-*-*,*"
+ font_name = "Sans 12"
}
# common default
@@ -77,7 +76,7 @@ style "slider"
style "ruler"
{
- font = '-adobe-helvetica-medium-r-normal--*-80-*-*-*-*-*-*'
+ font_name = 'Sans 8'
}
style "curve"
@@ -93,7 +92,7 @@ style "red-bar"
# override testgtk2, introduce the green color in the button list
style 'button_list' = 'button'
{
- font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
+ font_name = "Monospace 10"
bg[PRELIGHT] = { 0, 0.75, 0x00 }
}
widget "main window.*GtkScrolledWindow.*GtkButton*" style "button_list"
diff --git a/gtk/testgtkrc2 b/gtk/testgtkrc2
index 71d2891f4..62b3a1c7b 100644
--- a/gtk/testgtkrc2
+++ b/gtk/testgtkrc2
@@ -14,7 +14,7 @@
style 'main_buttons' = 'button'
{
- font = "-adobe-helvetica-medium-r-normal--*-100-*-*-*-*-*-*"
+ font_name = "Monospace 10"
bg[PRELIGHT] = { 0, 0, 0.75 }
}
diff --git a/gtk/testtext.c b/gtk/testtext.c
new file mode 100644
index 000000000..478a6dfb2
--- /dev/null
+++ b/gtk/testtext.c
@@ -0,0 +1,1261 @@
+#include <stdio.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unicode.h>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+
+typedef struct _Buffer Buffer;
+typedef struct _View View;
+
+static gint untitled_serial = 1;
+
+GSList *active_window_stack = NULL;
+
+struct _Buffer
+{
+ gint refcount;
+ GtkTextBuffer *buffer;
+ char *filename;
+ gint untitled_serial;
+};
+
+struct _View
+{
+ GtkWidget *window;
+ GtkWidget *text_view;
+ GtkAccelGroup *accel_group;
+ GtkItemFactory *item_factory;
+ Buffer *buffer;
+};
+
+static void push_active_window (GtkWindow *window);
+static void pop_active_window (void);
+static GtkWindow *get_active_window (void);
+
+static Buffer * create_buffer (void);
+static gboolean check_buffer_saved (Buffer *buffer);
+static gboolean save_buffer (Buffer *buffer);
+static gboolean save_as_buffer (Buffer *buffer);
+static char * buffer_pretty_name (Buffer *buffer);
+static void buffer_filename_set (Buffer *buffer);
+
+static View *view_from_widget (GtkWidget *widget);
+
+static View *create_view (Buffer *buffer);
+static void check_close_view (View *view);
+static void close_view (View *view);
+static void view_set_title (View *view);
+static void view_init_menus (View *view);
+
+GSList *buffers = NULL;
+GSList *views = NULL;
+
+static void
+push_active_window (GtkWindow *window)
+{
+ gtk_object_ref (GTK_OBJECT (window));
+ active_window_stack = g_slist_prepend (active_window_stack, window);
+}
+
+static void
+pop_active_window (void)
+{
+ gtk_object_unref (active_window_stack->data);
+ active_window_stack = g_slist_delete_link (active_window_stack, active_window_stack);
+}
+
+static GtkWindow *
+get_active_window (void)
+{
+ if (active_window_stack)
+ return active_window_stack->data;
+ else
+ return NULL;
+}
+
+/*
+ * Filesel utility function
+ */
+
+typedef gboolean (*FileselOKFunc) (const char *filename, gpointer data);
+
+static void
+filesel_ok_cb (GtkWidget *button, GtkWidget *filesel)
+{
+ FileselOKFunc ok_func = gtk_object_get_data (GTK_OBJECT (filesel), "ok-func");
+ gpointer data = gtk_object_get_data (GTK_OBJECT (filesel), "ok-data");
+ gint *result = gtk_object_get_data (GTK_OBJECT (filesel), "ok-result");
+
+ gtk_widget_hide (filesel);
+
+ if ((*ok_func) (gtk_file_selection_get_filename (GTK_FILE_SELECTION (filesel)), data))
+ {
+ gtk_widget_destroy (filesel);
+ *result = TRUE;
+ }
+ else
+ gtk_widget_show (filesel);
+}
+
+gboolean
+filesel_run (GtkWindow *parent,
+ const char *title,
+ const char *start_file,
+ FileselOKFunc func,
+ gpointer data)
+{
+ GtkWidget *filesel = gtk_file_selection_new (title);
+ gboolean result = FALSE;
+
+ if (!parent)
+ parent = get_active_window ();
+
+ if (parent)
+ gtk_window_set_transient_for (GTK_WINDOW (filesel), parent);
+
+ if (start_file)
+ gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), start_file);
+
+
+ gtk_object_set_data (GTK_OBJECT (filesel), "ok-func", func);
+ gtk_object_set_data (GTK_OBJECT (filesel), "ok-data", data);
+ gtk_object_set_data (GTK_OBJECT (filesel), "ok-result", &result);
+
+ gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (filesel_ok_cb), filesel);
+ gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->cancel_button),
+ "clicked",
+ GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (filesel));
+
+ gtk_signal_connect (GTK_OBJECT (filesel), "destroy",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+ gtk_window_set_modal (GTK_WINDOW (filesel), TRUE);
+
+ gtk_widget_show (filesel);
+ gtk_main ();
+
+ return result;
+}
+
+/*
+ * MsgBox utility functions
+ */
+
+static void
+msgbox_yes_cb (GtkWidget *widget, gboolean *result)
+{
+ *result = 0;
+ gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
+}
+
+static void
+msgbox_no_cb (GtkWidget *widget, gboolean *result)
+{
+ *result = 1;
+ gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
+}
+
+static gboolean
+msgbox_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
+{
+ if (event->keyval == GDK_Escape)
+ {
+ gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
+ gtk_object_destroy (GTK_OBJECT (widget));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+gint
+msgbox_run (GtkWindow *parent,
+ const char *message,
+ const char *yes_button,
+ const char *no_button,
+ const char *cancel_button,
+ gint default_index)
+{
+ gboolean result = -1;
+ GtkWidget *dialog;
+ GtkWidget *button;
+ GtkWidget *label;
+ GtkWidget *vbox;
+ GtkWidget *button_box;
+ GtkWidget *separator;
+
+ g_return_val_if_fail (message != NULL, FALSE);
+ g_return_val_if_fail (default_index >= 0 && default_index <= 1, FALSE);
+
+ if (!parent)
+ parent = get_active_window ();
+
+ /* Create a dialog
+ */
+ dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
+ if (parent)
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
+ gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
+
+ /* Quit our recursive main loop when the dialog is destroyed.
+ */
+ gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
+ GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
+
+ /* Catch Escape key presses and have them destroy the dialog
+ */
+ gtk_signal_connect (GTK_OBJECT (dialog), "key_press_event",
+ GTK_SIGNAL_FUNC (msgbox_key_press_cb), NULL);
+
+ /* Fill in the contents of the widget
+ */
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (dialog), vbox);
+
+ label = gtk_label_new (message);
+ gtk_misc_set_padding (GTK_MISC (label), 12, 12);
+ gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
+
+ separator = gtk_hseparator_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
+
+ button_box = gtk_hbutton_box_new ();
+ gtk_box_pack_start (GTK_BOX (vbox), button_box, FALSE, FALSE, 0);
+ gtk_container_set_border_width (GTK_CONTAINER (button_box), 8);
+
+
+ /* When Yes is clicked, call the msgbox_yes_cb
+ * This sets the result variable and destroys the dialog
+ */
+ if (yes_button)
+ {
+ button = gtk_button_new_with_label (yes_button);
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_container_add (GTK_CONTAINER (button_box), button);
+
+ if (default_index == 0)
+ gtk_widget_grab_default (button);
+
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (msgbox_yes_cb), &result);
+ }
+
+ /* When No is clicked, call the msgbox_no_cb
+ * This sets the result variable and destroys the dialog
+ */
+ if (no_button)
+ {
+ button = gtk_button_new_with_label (no_button);
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_container_add (GTK_CONTAINER (button_box), button);
+
+ if (default_index == 0)
+ gtk_widget_grab_default (button);
+
+ gtk_signal_connect (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (msgbox_no_cb), &result);
+ }
+
+ /* When Cancel is clicked, destroy the dialog
+ */
+ if (cancel_button)
+ {
+ button = gtk_button_new_with_label (cancel_button);
+ GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+ gtk_container_add (GTK_CONTAINER (button_box), button);
+
+ if (default_index == 1)
+ gtk_widget_grab_default (button);
+
+ gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
+ GTK_SIGNAL_FUNC (gtk_object_destroy), GTK_OBJECT (dialog));
+ }
+
+ gtk_widget_show_all (dialog);
+
+ /* Run a recursive main loop until a button is clicked
+ * or the user destroys the dialog through the window mananger */
+ gtk_main ();
+
+ return result;
+}
+
+/*
+ * Example buffer filling code
+ */
+static gint
+blink_timeout(gpointer data)
+{
+ GtkTextTag *tag;
+ static gboolean flip = FALSE;
+
+ tag = GTK_TEXT_TAG(data);
+
+ gtk_object_set(GTK_OBJECT(tag),
+ "foreground", flip ? "blue" : "purple",
+ NULL);
+
+ flip = !flip;
+
+ return TRUE;
+}
+
+static gint
+tag_event_handler(GtkTextTag *tag, GtkWidget *widget, GdkEvent *event,
+ const GtkTextIter *iter, gpointer user_data)
+{
+ gint char_index;
+
+ char_index = gtk_text_iter_get_char_index(iter);
+
+ switch (event->type)
+ {
+ case GDK_MOTION_NOTIFY:
+ printf("Motion event at char %d tag `%s'\n",
+ char_index, tag->name);
+ break;
+
+ case GDK_BUTTON_PRESS:
+ printf("Button press at char %d tag `%s'\n",
+ char_index, tag->name);
+ break;
+
+ case GDK_2BUTTON_PRESS:
+ printf("Double click at char %d tag `%s'\n",
+ char_index, tag->name);
+ break;
+
+ case GDK_3BUTTON_PRESS:
+ printf("Triple click at char %d tag `%s'\n",
+ char_index, tag->name);
+ break;
+
+ case GDK_BUTTON_RELEASE:
+ printf("Button release at char %d tag `%s'\n",
+ char_index, tag->name);
+ break;
+
+ case GDK_KEY_PRESS:
+ case GDK_KEY_RELEASE:
+ case GDK_ENTER_NOTIFY:
+ case GDK_LEAVE_NOTIFY:
+ case GDK_PROPERTY_NOTIFY:
+ case GDK_SELECTION_CLEAR:
+ case GDK_SELECTION_REQUEST:
+ case GDK_SELECTION_NOTIFY:
+ case GDK_PROXIMITY_IN:
+ case GDK_PROXIMITY_OUT:
+ case GDK_DRAG_ENTER:
+ case GDK_DRAG_LEAVE:
+ case GDK_DRAG_MOTION:
+ case GDK_DRAG_STATUS:
+ case GDK_DROP_START:
+ case GDK_DROP_FINISHED:
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+static void
+setup_tag(GtkTextTag *tag)
+{
+
+ gtk_signal_connect(GTK_OBJECT(tag),
+ "event",
+ GTK_SIGNAL_FUNC(tag_event_handler),
+ NULL);
+}
+
+static char *book_closed_xpm[] = {
+"16 16 6 1",
+" c None s None",
+". c black",
+"X c red",
+"o c yellow",
+"O c #808080",
+"# c white",
+" ",
+" .. ",
+" ..XX. ",
+" ..XXXXX. ",
+" ..XXXXXXXX. ",
+".ooXXXXXXXXX. ",
+"..ooXXXXXXXXX. ",
+".X.ooXXXXXXXXX. ",
+".XX.ooXXXXXX.. ",
+" .XX.ooXXX..#O ",
+" .XX.oo..##OO. ",
+" .XX..##OO.. ",
+" .X.#OO.. ",
+" ..O.. ",
+" .. ",
+" "};
+
+
+
+void
+fill_example_buffer (GtkTextBuffer *buffer)
+{
+ GtkTextIter iter, iter2;
+ GtkTextTag *tag;
+ GdkColor color;
+ GdkColor color2;
+ GdkPixmap *pixmap;
+ GdkBitmap *mask;
+ int i;
+ char *str;
+
+ tag = gtk_text_buffer_create_tag(buffer, "fg_blue");
+
+ /* gtk_timeout_add(1000, blink_timeout, tag); */
+
+ setup_tag(tag);
+
+ color.red = color.green = 0;
+ color.blue = 0xffff;
+ color2.red = 0xfff;
+ color2.blue = 0x0;
+ color2.green = 0;
+ gtk_object_set(GTK_OBJECT(tag),
+ "foreground_gdk", &color,
+ "background_gdk", &color2,
+ "font", "Sans 24",
+ NULL);
+
+ tag = gtk_text_buffer_create_tag(buffer, "fg_red");
+
+ setup_tag(tag);
+
+ color.blue = color.green = 0;
+ color.red = 0xffff;
+ gtk_object_set(GTK_OBJECT(tag),
+ "offset", -4,
+ "foreground_gdk", &color,
+ NULL);
+
+ tag = gtk_text_buffer_create_tag(buffer, "bg_green");
+
+ setup_tag(tag);
+
+ color.blue = color.red = 0;
+ color.green = 0xffff;
+ gtk_object_set(GTK_OBJECT(tag),
+ "background_gdk", &color,
+ "font", "Sans 10",
+ NULL);
+
+ tag = gtk_text_buffer_create_tag(buffer, "overstrike");
+
+ setup_tag(tag);
+
+ gtk_object_set(GTK_OBJECT(tag),
+ "overstrike", TRUE,
+ NULL);
+
+
+ tag = gtk_text_buffer_create_tag(buffer, "underline");
+
+ setup_tag(tag);
+
+ gtk_object_set(GTK_OBJECT(tag),
+ "underline", PANGO_UNDERLINE_SINGLE,
+ NULL);
+
+ setup_tag(tag);
+
+ gtk_object_set(GTK_OBJECT(tag),
+ "underline", PANGO_UNDERLINE_SINGLE,
+ NULL);
+
+ tag = gtk_text_buffer_create_tag(buffer, "centered");
+
+ gtk_object_set(GTK_OBJECT(tag),
+ "justify", GTK_JUSTIFY_CENTER,
+ NULL);
+
+ tag = gtk_text_buffer_create_tag(buffer, "rtl_quote");
+
+ gtk_object_set(GTK_OBJECT(tag),
+ "wrap_mode", GTK_WRAPMODE_WORD,
+ "direction", GTK_TEXT_DIR_RTL,
+ "left_wrapped_line_margin", 20,
+ "left_margin", 20,
+ "right_margin", 20,
+ NULL);
+
+ pixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL,
+ gtk_widget_get_default_colormap(),
+ &mask,
+ NULL, book_closed_xpm);
+
+ g_assert(pixmap != NULL);
+
+ i = 0;
+ while (i < 100)
+ {
+ gtk_text_buffer_get_iter_at_char(buffer, &iter, 0);
+
+ gtk_text_buffer_insert_pixmap (buffer, &iter, pixmap, mask);
+
+ str = g_strdup_printf("%d Hello World! blah blah blah blah blah blah blah blah blah blah blah blah\nwoo woo woo woo woo woo woo woo woo woo woo woo woo woo woo\n",
+ i);
+
+ gtk_text_buffer_insert(buffer, &iter, str, -1);
+
+ g_free(str);
+
+ gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 0, 5);
+
+ gtk_text_buffer_insert(buffer, &iter,
+ "(Hello World!)\nfoo foo Hello this is some text we are using to text word wrap. It has punctuation! gee; blah - hmm, great.\nnew line with a significant quantity of text on it. This line really does contain some text. More text! More text! More text!\n"
+ /* This is UTF8 stuff, Emacs doesn't
+ really know how to display it */
+ "German (Deutsch Süd) Grüß Gott Greek (Ελληνικά) Γειά σας Hebrew שלום Japanese (日本語)\n", -1);
+
+ gtk_text_buffer_create_mark (buffer, "tmp_mark", &iter, TRUE);
+
+#if 1
+ gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 0, 6);
+ gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 0, 13);
+
+ gtk_text_buffer_apply_tag(buffer, "fg_blue", &iter, &iter2);
+
+ gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 1, 10);
+ gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 1, 16);
+
+ gtk_text_buffer_apply_tag(buffer, "underline", &iter, &iter2);
+
+ gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 1, 14);
+ gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 1, 24);
+
+ gtk_text_buffer_apply_tag(buffer, "overstrike", &iter, &iter2);
+
+ gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 0, 9);
+ gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 0, 16);
+
+ gtk_text_buffer_apply_tag(buffer, "bg_green", &iter, &iter2);
+
+ gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 4, 2);
+ gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 4, 10);
+
+ gtk_text_buffer_apply_tag(buffer, "bg_green", &iter, &iter2);
+
+ gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 4, 8);
+ gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 4, 15);
+
+ gtk_text_buffer_apply_tag(buffer, "fg_red", &iter, &iter2);
+#endif
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter, "tmp_mark");
+ gtk_text_buffer_insert (buffer, &iter, "Centered text!\n", -1);
+
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter2, "tmp_mark");
+ gtk_text_buffer_apply_tag (buffer, "centered", &iter2, &iter);
+
+ gtk_text_buffer_move_mark (buffer, "tmp_mark", &iter);
+ gtk_text_buffer_insert (buffer, &iter, "Word wrapped, Right-to-left Quote\n", -1);
+ gtk_text_buffer_insert (buffer, &iter, "وقد بدأ ثلاث من أكثر المؤسسات تقدما في شبكة اكسيون برامجها كمنظمات لا تسعى للربح، ثم تحولت في السنوات الخمس الماضية إلى مؤسسات مالية منظمة، وباتت جزءا من النظام المالي في بلدانها، ولكنها تتخصص في خدمة قطاع المشروعات الصغيرة. وأحد أكثر هذه المؤسسات نجاحا هو »بانكوسول« في بوليفيا.\n", -1);
+ gtk_text_buffer_get_iter_at_mark (buffer, &iter2, "tmp_mark");
+ gtk_text_buffer_apply_tag (buffer, "rtl_quote", &iter2, &iter);
+
+ ++i;
+ }
+
+ gdk_pixmap_unref(pixmap);
+ if (mask)
+ gdk_bitmap_unref(mask);
+
+ printf("%d lines %d chars\n",
+ gtk_text_buffer_get_line_count(buffer),
+ gtk_text_buffer_get_char_count(buffer));
+
+ gtk_text_buffer_set_modified (buffer, FALSE);
+}
+
+gboolean
+fill_file_buffer (GtkTextBuffer *buffer, const char *filename)
+{
+ FILE* f;
+ gchar buf[2048];
+ gint remaining = 0;
+ GtkTextIter iter, end;
+
+ f = fopen(filename, "r");
+
+ if (f == NULL)
+ {
+ gchar *err = g_strdup_printf ("Cannot open file '%s': %s",
+ filename, g_strerror (errno));
+ msgbox_run (NULL, err, "OK", NULL, NULL, 0);
+ g_free (err);
+ return FALSE;
+ }
+
+ gtk_text_buffer_get_iter_at_char(buffer, &iter, 0);
+ while (!feof (f))
+ {
+ gint count;
+ char *leftover, *next;
+ unicode_char_t wc;
+ int to_read = 2047 - remaining;
+
+ count = fread (buf + remaining, 1, to_read, f);
+ buf[count + remaining] = '\0';
+
+ leftover = next = buf;
+ while (next)
+ {
+ leftover = next;
+ if (!*leftover)
+ break;
+
+ next = unicode_get_utf8 (next, &wc);
+ }
+
+ gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf);
+
+ remaining = buf + remaining + count - leftover;
+ g_memmove (buf, leftover, remaining);
+
+ if (remaining > 6 || count < to_read)
+ break;
+ }
+
+ if (remaining)
+ {
+ gchar *err = g_strdup_printf ("Invalid UTF-8 data encountered reading file '%s'", filename);
+ msgbox_run (NULL, err, "OK", NULL, NULL, 0);
+ g_free (err);
+ }
+
+ /* We had a newline in the buffer to begin with. (The buffer always contains
+ * a newline, so we delete to the end of the buffer to clean up.
+ */
+ gtk_text_buffer_get_last_iter (buffer, &end);
+ gtk_text_buffer_delete (buffer, &iter, &end);
+
+ gtk_text_buffer_set_modified (buffer, FALSE);
+
+ return TRUE;
+}
+
+static gint
+delete_event_cb(GtkWidget *window, GdkEventAny *event, gpointer data)
+{
+ View *view = view_from_widget (window);
+
+ push_active_window (GTK_WINDOW (window));
+ check_close_view (view);
+ pop_active_window ();
+
+ return TRUE;
+}
+
+/*
+ * Menu callbacks
+ */
+
+static View *
+get_empty_view (View *view)
+{
+ if (!view->buffer->filename &&
+ !gtk_text_buffer_modified (view->buffer->buffer))
+ return view;
+ else
+ return create_view (create_buffer ());
+}
+
+static View *
+view_from_widget (GtkWidget *widget)
+{
+ GtkWidget *app;
+
+ if (GTK_IS_MENU_ITEM (widget))
+ {
+ GtkItemFactory *item_factory = gtk_item_factory_from_widget (widget);
+ return gtk_object_get_data (GTK_OBJECT (item_factory), "view");
+ }
+ else
+ {
+ GtkWidget *app = gtk_widget_get_toplevel (widget);
+ return gtk_object_get_data (GTK_OBJECT (app), "view");
+ }
+}
+
+static void
+do_new (gpointer callback_data,
+ guint callback_action,
+ GtkWidget *widget)
+{
+ create_view (create_buffer ());
+}
+
+static void
+do_new_view (gpointer callback_data,
+ guint callback_action,
+ GtkWidget *widget)
+{
+ View *view = view_from_widget (widget);
+
+ create_view (view->buffer);
+}
+
+gboolean
+open_ok_func (const char *filename, gpointer data)
+{
+ View *view = data;
+ View *new_view = get_empty_view (view);
+
+ if (!fill_file_buffer (new_view->buffer->buffer, filename))
+ {
+ if (new_view != view)
+ close_view (new_view);
+ return FALSE;
+ }
+ else
+ {
+ g_free (new_view->buffer->filename);
+ new_view->buffer->filename = g_strdup (filename);
+ buffer_filename_set (new_view->buffer);
+
+ return TRUE;
+ }
+}
+
+static void
+do_open (gpointer callback_data,
+ guint callback_action,
+ GtkWidget *widget)
+{
+ View *view = view_from_widget (widget);
+
+ push_active_window (GTK_WINDOW (view->window));
+ filesel_run (NULL, "Open File", NULL, open_ok_func, view);
+ pop_active_window ();
+}
+
+static void
+do_save_as (gpointer callback_data,
+ guint callback_action,
+ GtkWidget *widget)
+{
+ View *view = view_from_widget (widget);
+
+ push_active_window (GTK_WINDOW (view->window));
+ save_as_buffer (view->buffer);
+ pop_active_window ();
+}
+
+static void
+do_save (gpointer callback_data,
+ guint callback_action,
+ GtkWidget *widget)
+{
+ View *view = view_from_widget (widget);
+
+ push_active_window (GTK_WINDOW (view->window));
+ if (!view->buffer->filename)
+ do_save_as (callback_data, callback_action, widget);
+ else
+ save_buffer (view->buffer);
+ pop_active_window ();
+}
+
+static void
+do_close (gpointer callback_data,
+ guint callback_action,
+ GtkWidget *widget)
+{
+ View *view = view_from_widget (widget);
+
+ push_active_window (GTK_WINDOW (view->window));
+ check_close_view (view);
+ pop_active_window ();
+}
+
+static void
+do_exit (gpointer callback_data,
+ guint callback_action,
+ GtkWidget *widget)
+{
+ View *view = view_from_widget (widget);
+
+ GSList *tmp_list = buffers;
+
+ push_active_window (GTK_WINDOW (view->window));
+ while (tmp_list)
+ {
+ if (!check_buffer_saved (tmp_list->data))
+ return;
+
+ tmp_list = tmp_list->next;
+ }
+
+ gtk_main_quit();
+ pop_active_window ();
+}
+
+static void
+do_example (gpointer callback_data,
+ guint callback_action,
+ GtkWidget *widget)
+{
+ View *view = view_from_widget (widget);
+ View *new_view;
+
+ new_view = get_empty_view (view);
+
+ fill_example_buffer (new_view->buffer->buffer);
+}
+
+static void
+do_wrap_changed (gpointer callback_data,
+ guint callback_action,
+ GtkWidget *widget)
+{
+ View *view = view_from_widget (widget);
+
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), callback_action);
+}
+
+static void
+do_direction_changed (gpointer callback_data,
+ guint callback_action,
+ GtkWidget *widget)
+{
+ View *view = view_from_widget (widget);
+
+ gtk_widget_set_direction (view->text_view, callback_action);
+ gtk_widget_queue_resize (view->text_view);
+}
+
+static void
+view_init_menus (View *view)
+{
+ GtkTextDirection direction = gtk_widget_get_direction (view->text_view);
+ GtkWrapMode wrap_mode = gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view->text_view));
+ GtkWidget *menu_item = NULL;
+
+ switch (direction)
+ {
+ case GTK_TEXT_DIR_LTR:
+ menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Left-to-Right");
+ break;
+ case GTK_TEXT_DIR_RTL:
+ menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Right-to-Left");
+ break;
+ default:
+ break;
+ }
+
+ if (menu_item)
+ gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
+
+ switch (wrap_mode)
+ {
+ case GTK_WRAPMODE_NONE:
+ menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Off");
+ break;
+ case GTK_WRAPMODE_WORD:
+ menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Words");
+ break;
+ default:
+ break;
+ }
+
+ if (menu_item)
+ gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
+}
+
+static GtkItemFactoryEntry menu_items[] =
+{
+ { "/_File", NULL, 0, 0, "<Branch>" },
+ { "/File/_New", "<control>N", do_new, 0, NULL },
+ { "/File/New _View", NULL, do_new_view, 0, NULL },
+ { "/File/_Open", "<control>O", do_open, 0, NULL },
+ { "/File/_Save", "<control>S", do_save, 0, NULL },
+ { "/File/Save _As...", NULL, do_save_as, 0, NULL },
+ { "/File/sep1", NULL, 0, 0, "<Separator>" },
+ { "/File/_Close", "<control>W" , do_close, 0, NULL },
+ { "/File/E_xit", "<control>Q" , do_exit, 0, NULL },
+
+ { "/_Settings", NULL, 0, 0, "<Branch>" },
+ { "/Settings/Wrap _Off", NULL, do_wrap_changed, GTK_WRAPMODE_NONE, "<RadioItem>" },
+ { "/Settings/Wrap _Words", NULL, do_wrap_changed, GTK_WRAPMODE_WORD, "/Settings/Wrap Off" },
+ { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
+ { "/Settings/Left-to-Right", NULL, do_direction_changed, GTK_TEXT_DIR_LTR, "<RadioItem>" },
+ { "/Settings/Right-to-Left", NULL, do_direction_changed, GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
+
+ { "/_Test", NULL, 0, 0, "<Branch>" },
+ { "/Test/_Example", NULL, do_example, 0, NULL },
+};
+
+static gboolean
+save_buffer (Buffer *buffer)
+{
+ GtkTextIter start, end;
+ gchar *chars;
+ gboolean result = FALSE;
+ gboolean have_backup = FALSE;
+ gchar *bak_filename;
+ FILE *file;
+
+ g_return_val_if_fail (buffer->filename != NULL, FALSE);
+
+ bak_filename = g_strconcat (buffer->filename, "~", NULL);
+
+ if (rename (buffer->filename, bak_filename) != 0)
+ {
+ if (errno != ENOENT)
+ {
+ gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
+ buffer->filename, bak_filename, g_strerror (errno));
+ msgbox_run (NULL, err, "OK", NULL, NULL, 0);
+ g_free (err);
+ }
+
+ return FALSE;
+ }
+ else
+ have_backup = TRUE;
+
+ file = fopen (buffer->filename, "w");
+ if (!file)
+ {
+ gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
+ buffer->filename, bak_filename, g_strerror (errno));
+ msgbox_run (NULL, err, "OK", NULL, NULL, 0);
+ }
+ else
+ {
+ gtk_text_buffer_get_iter_at_char (buffer->buffer, &start, 0);
+ gtk_text_buffer_get_last_iter (buffer->buffer, &end);
+
+ chars = gtk_text_buffer_get_slice (buffer->buffer, &start, &end, FALSE);
+
+ if (fputs (chars, file) == EOF ||
+ fclose (file) == EOF)
+ {
+ gchar *err = g_strdup_printf ("Error writing to '%s': %s",
+ buffer->filename, g_strerror (errno));
+ msgbox_run (NULL, err, "OK", NULL, NULL, 0);
+ g_free (err);
+ }
+ else
+ {
+ /* Success
+ */
+ result = TRUE;
+ gtk_text_buffer_set_modified (buffer->buffer, FALSE);
+ }
+
+ g_free (chars);
+ }
+
+ if (!result && have_backup)
+ {
+ if (rename (bak_filename, buffer->filename) != 0)
+ {
+ gchar *err = g_strdup_printf ("Error restoring backup file '%s' to '%s': %s\nBackup left as '%s'",
+ buffer->filename, bak_filename, g_strerror (errno), bak_filename);
+ msgbox_run (NULL, err, "OK", NULL, NULL, 0);
+ g_free (err);
+ }
+ }
+
+ g_free (bak_filename);
+
+ return result;
+}
+
+static gboolean
+save_as_ok_func (const char *filename, gpointer data)
+{
+ Buffer *buffer = data;
+ char *old_filename = buffer->filename;
+
+ if (!buffer->filename || strcmp (filename, buffer->filename) != 0)
+ {
+ struct stat statbuf;
+
+ if (stat(filename, &statbuf) == 0)
+ {
+ gchar *err = g_strdup_printf ("Ovewrite existing file '%s'?", filename);
+ gint result = msgbox_run (NULL, err, "Yes", "No", NULL, 1);
+ g_free (err);
+
+ if (result != 0)
+ return FALSE;
+ }
+ }
+
+ buffer->filename = g_strdup (filename);
+
+ if (save_buffer (buffer))
+ {
+ g_free (old_filename);
+ buffer_filename_set (buffer);
+ return TRUE;
+ }
+ else
+ {
+ g_free (buffer->filename);
+ buffer->filename = old_filename;
+ return FALSE;
+ }
+}
+
+static gboolean
+save_as_buffer (Buffer *buffer)
+{
+ return filesel_run (NULL, "Save File", NULL, save_as_ok_func, buffer);
+}
+
+static gboolean
+check_buffer_saved (Buffer *buffer)
+{
+ if (gtk_text_buffer_modified (buffer->buffer))
+ {
+ char *pretty_name = buffer_pretty_name (buffer);
+ char *msg = g_strdup_printf ("Save changes to '%s'?", pretty_name);
+ gint result;
+
+ g_free (pretty_name);
+
+ result = msgbox_run (NULL, msg, "Yes", "No", "Cancel", 0);
+ g_free (msg);
+
+ if (result == 0)
+ return save_as_buffer (buffer);
+ else if (result == 1)
+ return TRUE;
+ else
+ return FALSE;
+ }
+ else
+ return TRUE;
+}
+
+static Buffer *
+create_buffer (void)
+{
+ Buffer *buffer;
+
+ buffer = g_new (Buffer, 1);
+
+ buffer->buffer = gtk_text_buffer_new (NULL);
+ gtk_object_ref (GTK_OBJECT (buffer->buffer));
+ gtk_object_sink (GTK_OBJECT (buffer->buffer));
+
+ buffer->refcount = 1;
+ buffer->filename = NULL;
+ buffer->untitled_serial = -1;
+
+ buffers = g_slist_prepend (buffers, buffer);
+
+ return buffer;
+}
+
+static char *
+buffer_pretty_name (Buffer *buffer)
+{
+ if (buffer->filename)
+ {
+ char *p;
+ char *result = g_strdup (g_basename (buffer->filename));
+ p = strchr (result, '/');
+ if (p)
+ *p = '\0';
+
+ return result;
+ }
+ else
+ {
+ if (buffer->untitled_serial == -1)
+ buffer->untitled_serial = untitled_serial++;
+
+ if (buffer->untitled_serial == 1)
+ return g_strdup ("Untitled");
+ else
+ return g_strdup_printf ("Untitled #%d", buffer->untitled_serial);
+ }
+}
+
+static void
+buffer_filename_set (Buffer *buffer)
+{
+ GSList *tmp_list = views;
+
+ while (tmp_list)
+ {
+ View *view = tmp_list->data;
+
+ if (view->buffer == buffer)
+ view_set_title (view);
+
+ tmp_list = tmp_list->next;
+ }
+}
+
+static void
+buffer_ref (Buffer *buffer)
+{
+ buffer->refcount++;
+}
+
+static void
+buffer_unref (Buffer *buffer)
+{
+ buffer->refcount--;
+ if (buffer->refcount == 0)
+ {
+ buffers = g_slist_remove (buffers, buffer);
+ gtk_object_unref (GTK_OBJECT (buffer->buffer));
+ g_free (buffer->filename);
+ g_free (buffer);
+ }
+}
+
+static void
+close_view (View *view)
+{
+ views = g_slist_remove (views, view);
+ buffer_unref (view->buffer);
+ gtk_widget_destroy (view->window);
+ g_object_unref (G_OBJECT (view->item_factory));
+
+ g_free (view);
+
+ if (!views)
+ gtk_main_quit();
+}
+
+static void
+check_close_view (View *view)
+{
+ if (view->buffer->refcount > 1 ||
+ check_buffer_saved (view->buffer))
+ close_view (view);
+}
+
+static void
+view_set_title (View *view)
+{
+ char *pretty_name = buffer_pretty_name (view->buffer);
+ char *title = g_strconcat ("testtext - ", pretty_name, NULL);
+
+ gtk_window_set_title (GTK_WINDOW (view->window), title);
+
+ g_free (pretty_name);
+ g_free (title);
+}
+
+static View *
+create_view (Buffer *buffer)
+{
+ View *view;
+
+ GtkWidget *sw;
+ GtkWidget *vbox;
+
+ view = g_new0 (View, 1);
+ views = g_slist_prepend (views, view);
+
+ view->buffer = buffer;
+ buffer_ref (buffer);
+
+ view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_object_set_data (GTK_OBJECT (view->window), "view", view);
+
+ gtk_signal_connect (GTK_OBJECT (view->window), "delete_event",
+ GTK_SIGNAL_FUNC(delete_event_cb), NULL);
+
+ view->accel_group = gtk_accel_group_new ();
+ view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
+ gtk_object_set_data (GTK_OBJECT (view->item_factory), "view", view);
+
+ gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
+
+ gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
+
+ vbox = gtk_vbox_new (FALSE, 0);
+ gtk_container_add (GTK_CONTAINER (view->window), vbox);
+
+ gtk_box_pack_start (GTK_BOX (vbox),
+ gtk_item_factory_get_widget (view->item_factory, "<main>"),
+ FALSE, FALSE, 0);
+
+ sw = gtk_scrolled_window_new(NULL, NULL);
+ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
+ GTK_POLICY_AUTOMATIC,
+ GTK_POLICY_AUTOMATIC);
+
+ view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
+ gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), GTK_WRAPMODE_WORD);
+
+ gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
+ gtk_container_add(GTK_CONTAINER(sw), view->text_view);
+
+ gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
+
+ gtk_widget_grab_focus(view->text_view);
+
+ view_set_title (view);
+ view_init_menus (view);
+
+ gtk_widget_show_all (view->window);
+ return view;
+}
+
+int
+main(int argc, char** argv)
+{
+ Buffer *buffer;
+ View *view;
+ int i;
+
+ gtk_init(&argc, &argv);
+
+ buffer = create_buffer ();
+ view = create_view (buffer);
+ buffer_unref (buffer);
+
+ push_active_window (GTK_WINDOW (view->window));
+ for (i=1; i < argc; i++)
+ {
+ char *filename;
+
+ /* Quick and dirty canonicalization - better should be in GLib
+ */
+
+ if (!g_path_is_absolute (argv[i]))
+ {
+ char *cwd = g_get_current_dir ();
+ filename = g_strconcat (cwd, "/", argv[i], NULL);
+ g_free (cwd);
+ }
+ else
+ filename = argv[i];
+
+ open_ok_func (filename, view);
+
+ if (filename != argv[i])
+ g_free (filename);
+ }
+ pop_active_window ();
+
+ gtk_main();
+
+ return 0;
+}
+
+