[glabels/vala] Initial implementation of LabelObjectText.



commit e05f32d7e382617c64f181f064ebe390f57cbe1b
Author: Jim Evins <evins snaught com>
Date:   Sun Jul 22 21:38:11 2012 -0400

    Initial implementation of LabelObjectText.
    
    Still needs a lot of work.

 data/ui/object_editor.ui       |  635 ++++++++++++++++++++++++++++++++++++---
 glabels/Makefile.am            |    7 +-
 glabels/color_swatch.vala      |    2 +-
 glabels/font_sample.vala       |    2 +-
 glabels/handle.vala            |    2 +-
 glabels/label_object.vala      |   35 +++
 glabels/label_object_text.vala |  293 ++++++++++++++++++
 glabels/mini_preview.vala      |    2 +-
 glabels/object_editor.vala     |  158 +++++++---
 glabels/outline.vala           |   85 ++++++
 glabels/text_line.vala         |   80 +++++
 glabels/text_lines.vala        |   60 ++++
 glabels/text_node.vala         |  264 +++++++++++++++++
 glabels/ui.vala                |    7 +-
 glabels/valign_type.vala       |   28 ++
 glabels/view.vala              |   28 ++-
 glabels/xml_label.vala         |   56 ++++-
 17 files changed, 1637 insertions(+), 107 deletions(-)
---
diff --git a/data/ui/object_editor.ui b/data/ui/object_editor.ui
index a9731f5..11ef3bb 100644
--- a/data/ui/object_editor.ui
+++ b/data/ui/object_editor.ui
@@ -1,27 +1,19 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <requires lib="gtk+" version="2.20"/>
-  <object class="GtkSizeGroup" id="color_box_sizegroup">
-    <widgets>
-      <widget name="shadow_color_box"/>
-      <widget name="line_color_box"/>
-      <widget name="fill_color_box"/>
-    </widgets>
+  <!-- interface-requires gtk+ 3.0 -->
+  <object class="GtkAdjustment" id="font_size_adjustment">
+    <property name="lower">1</property>
+    <property name="upper">100</property>
+    <property name="value">12</property>
+    <property name="step_increment">0.5</property>
+    <property name="page_increment">10</property>
   </object>
-  <object class="GtkSizeGroup" id="label_sizegroup">
-    <widgets>
-      <widget name="line_w_label"/>
-      <widget name="line_color_label"/>
-      <widget name="fill_color_label"/>
-      <widget name="pos_x_label"/>
-      <widget name="pos_y_label"/>
-      <widget name="size_w_label"/>
-      <widget name="size_h_label"/>
-      <widget name="label40"/>
-      <widget name="label41"/>
-      <widget name="label45"/>
-      <widget name="label46"/>
-    </widgets>
+  <object class="GtkAdjustment" id="line_spacing_adjustment">
+    <property name="lower">1</property>
+    <property name="upper">5</property>
+    <property name="value">1</property>
+    <property name="step_increment">0.10000000000000001</property>
+    <property name="page_increment">10</property>
   </object>
   <object class="GtkAdjustment" id="line_width_adjustment">
     <property name="lower">0.25</property>
@@ -30,16 +22,6 @@
     <property name="step_increment">0.25</property>
     <property name="page_increment">1</property>
   </object>
-  <object class="GtkAdjustment" id="pos_x_adjustment">
-    <property name="upper">100</property>
-    <property name="step_increment">0.01</property>
-    <property name="page_increment">1</property>
-  </object>
-  <object class="GtkAdjustment" id="pos_y_adjustment">
-    <property name="upper">100</property>
-    <property name="step_increment">0.01</property>
-    <property name="page_increment">1</property>
-  </object>
   <object class="GtkAdjustment" id="shadow_opacity_adjustment">
     <property name="upper">100</property>
     <property name="value">1</property>
@@ -119,6 +101,536 @@
             <property name="can_focus">True</property>
             <property name="enable_popup">True</property>
             <child>
+              <object class="GtkBox" id="text_page_box">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="valign">start</property>
+                <property name="hexpand">True</property>
+                <property name="border_width">6</property>
+                <property name="orientation">vertical</property>
+                <property name="spacing">12</property>
+                <child>
+                  <object class="GtkFrame" id="frame9">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">none</property>
+                    <child>
+                      <object class="GtkAlignment" id="alignment10">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <object class="GtkBox" id="box2">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="orientation">vertical</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <object class="GtkBox" id="box3">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="spacing">3</property>
+                                <child>
+                                  <object class="GtkBox" id="text_font_family_box">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="orientation">vertical</property>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkSpinButton" id="text_font_size_spin">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="has_tooltip">True</property>
+                                    <property name="tooltip_markup" translatable="yes">Font size</property>
+                                    <property name="tooltip_text" translatable="yes">Font size</property>
+                                    <property name="invisible_char">â</property>
+                                    <property name="invisible_char_set">True</property>
+                                    <property name="progress_pulse_step">0.5</property>
+                                    <property name="adjustment">font_size_adjustment</property>
+                                    <property name="digits">1</property>
+                                    <property name="numeric">True</property>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkBox" id="box9">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="spacing">3</property>
+                                <child>
+                                  <object class="GtkToggleButton" id="text_font_bold_toggle">
+                                    <property name="use_action_appearance">False</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">True</property>
+                                    <property name="has_tooltip">True</property>
+                                    <property name="tooltip_markup" translatable="yes">Bold</property>
+                                    <property name="tooltip_text" translatable="yes">Bold</property>
+                                    <property name="use_action_appearance">False</property>
+                                    <child>
+                                      <object class="GtkImage" id="image1">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="stock">gtk-bold</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkToggleButton" id="togglebutton1">
+                                    <property name="use_action_appearance">False</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">True</property>
+                                    <property name="has_tooltip">True</property>
+                                    <property name="tooltip_markup" translatable="yes">Italic</property>
+                                    <property name="tooltip_text" translatable="yes">Italic</property>
+                                    <property name="use_action_appearance">False</property>
+                                    <child>
+                                      <object class="GtkImage" id="image2">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="stock">gtk-italic</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkToggleButton" id="togglebutton2">
+                                    <property name="use_action_appearance">False</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">True</property>
+                                    <property name="has_tooltip">True</property>
+                                    <property name="tooltip_markup" translatable="yes">Underline</property>
+                                    <property name="tooltip_text" translatable="yes">Underline</property>
+                                    <property name="use_action_appearance">False</property>
+                                    <child>
+                                      <object class="GtkImage" id="image3">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="stock">gtk-underline</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">2</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkBox" id="text_color_box">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="orientation">vertical</property>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">3</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel" id="label13">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Font/Appearance&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkFrame" id="frame6">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">none</property>
+                    <child>
+                      <object class="GtkAlignment" id="alignment7">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <object class="GtkBox" id="box4">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="spacing">12</property>
+                            <child>
+                              <object class="GtkBox" id="box5">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="spacing">3</property>
+                                <child>
+                                  <object class="GtkToggleButton" id="text_align_left_toggle">
+                                    <property name="use_action_appearance">False</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">True</property>
+                                    <property name="use_action_appearance">False</property>
+                                    <child>
+                                      <object class="GtkImage" id="image4">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="stock">gtk-justify-left</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkToggleButton" id="text_align_center_toggle">
+                                    <property name="use_action_appearance">False</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">True</property>
+                                    <property name="use_action_appearance">False</property>
+                                    <child>
+                                      <object class="GtkImage" id="image5">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="stock">gtk-justify-center</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkToggleButton" id="text_align_right_toggle">
+                                    <property name="use_action_appearance">False</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">True</property>
+                                    <property name="use_action_appearance">False</property>
+                                    <child>
+                                      <object class="GtkImage" id="image6">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="stock">gtk-justify-right</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">2</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkBox" id="box6">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="spacing">3</property>
+                                <child>
+                                  <object class="GtkToggleButton" id="text_align_top_toggle">
+                                    <property name="use_action_appearance">False</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">True</property>
+                                    <property name="use_action_appearance">False</property>
+                                    <child>
+                                      <object class="GtkImage" id="image7">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="icon_name">glabels-align-text-top</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkToggleButton" id="text_align_middle_toggle">
+                                    <property name="use_action_appearance">False</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">True</property>
+                                    <property name="use_action_appearance">False</property>
+                                    <child>
+                                      <object class="GtkImage" id="image8">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="icon_name">glabels-align-text-middle</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">1</property>
+                                  </packing>
+                                </child>
+                                <child>
+                                  <object class="GtkToggleButton" id="text_align_bottom_toggle">
+                                    <property name="use_action_appearance">False</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="receives_default">True</property>
+                                    <property name="use_action_appearance">False</property>
+                                    <child>
+                                      <object class="GtkImage" id="image9">
+                                        <property name="visible">True</property>
+                                        <property name="can_focus">False</property>
+                                        <property name="icon_name">glabels-align-text-bottom</property>
+                                      </object>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">True</property>
+                                    <property name="position">2</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel" id="label10">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Alignment&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkFrame" id="frame7">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">none</property>
+                    <child>
+                      <object class="GtkAlignment" id="alignment8">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <object class="GtkBox" id="box7">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <child>
+                              <object class="GtkSpinButton" id="text_line_spacing_spin">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="has_tooltip">True</property>
+                                <property name="tooltip_markup" translatable="yes">Font size</property>
+                                <property name="tooltip_text" translatable="yes">Line spacing</property>
+                                <property name="invisible_char">â</property>
+                                <property name="invisible_char_set">True</property>
+                                <property name="progress_pulse_step">0.10000000149011612</property>
+                                <property name="adjustment">line_spacing_adjustment</property>
+                                <property name="digits">1</property>
+                                <property name="numeric">True</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel" id="label11">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Line spacing&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkFrame" id="frame8">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="vexpand">True</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">none</property>
+                    <child>
+                      <object class="GtkAlignment" id="alignment9">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="left_padding">12</property>
+                        <child>
+                          <object class="GtkBox" id="box8">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="orientation">vertical</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <object class="GtkScrolledWindow" id="scrolledwindow1">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="vexpand">True</property>
+                                <property name="shadow_type">in</property>
+                                <child>
+                                  <object class="GtkTextView" id="text_text_view">
+                                    <property name="width_request">232</property>
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">True</property>
+                                    <property name="vexpand">True</property>
+                                  </object>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">True</property>
+                                <property name="fill">True</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkBox" id="box1">
+                                <property name="visible">True</property>
+                                <property name="can_focus">False</property>
+                                <property name="halign">center</property>
+                                <property name="valign">start</property>
+                                <property name="hexpand">True</property>
+                                <child>
+                                  <object class="GtkBox" id="text_insert_field_box">
+                                    <property name="visible">True</property>
+                                    <property name="can_focus">False</property>
+                                    <property name="valign">start</property>
+                                    <property name="orientation">vertical</property>
+                                    <child>
+                                      <placeholder/>
+                                    </child>
+                                  </object>
+                                  <packing>
+                                    <property name="expand">False</property>
+                                    <property name="fill">False</property>
+                                    <property name="position">0</property>
+                                  </packing>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="fill">False</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                    </child>
+                    <child type="label">
+                      <object class="GtkLabel" id="label12">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="label" translatable="yes">&lt;b&gt;Editor&lt;/b&gt;</property>
+                        <property name="use_markup">True</property>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">4</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+            <child type="tab">
+              <object class="GtkLabel" id="label8">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Text</property>
+              </object>
+              <packing>
+                <property name="tab_fill">False</property>
+              </packing>
+            </child>
+            <child>
               <object class="GtkBox" id="line_fill_page_box">
                 <property name="visible">True</property>
                 <property name="can_focus">False</property>
@@ -145,7 +657,6 @@
                             <property name="hexpand">True</property>
                             <property name="row_spacing">6</property>
                             <property name="column_spacing">12</property>
-                            <property name="n_rows">2</property>
                             <child>
                               <object class="GtkLabel" id="line_w_label">
                                 <property name="visible">True</property>
@@ -221,9 +732,6 @@
                                 <property name="height">1</property>
                               </packing>
                             </child>
-                            <child>
-                              <placeholder/>
-                            </child>
                           </object>
                         </child>
                       </object>
@@ -261,7 +769,6 @@
                             <property name="hexpand">True</property>
                             <property name="row_spacing">6</property>
                             <property name="column_spacing">12</property>
-                            <property name="n_rows">1</property>
                             <child>
                               <object class="GtkLabel" id="fill_color_label">
                                 <property name="visible">True</property>
@@ -291,9 +798,6 @@
                                 <property name="height">1</property>
                               </packing>
                             </child>
-                            <child>
-                              <placeholder/>
-                            </child>
                           </object>
                         </child>
                       </object>
@@ -314,6 +818,9 @@
                   </packing>
                 </child>
               </object>
+              <packing>
+                <property name="position">1</property>
+              </packing>
             </child>
             <child type="tab">
               <object class="GtkLabel" id="label3">
@@ -323,6 +830,7 @@
                 <property name="label" translatable="yes">Line/Fill</property>
               </object>
               <packing>
+                <property name="position">1</property>
                 <property name="tab_fill">False</property>
               </packing>
             </child>
@@ -352,7 +860,6 @@
                             <property name="can_focus">False</property>
                             <property name="row_spacing">6</property>
                             <property name="column_spacing">12</property>
-                            <property name="n_rows">2</property>
                             <child>
                               <object class="GtkLabel" id="pos_x_label">
                                 <property name="width_request">50</property>
@@ -484,7 +991,6 @@
                             <property name="can_focus">False</property>
                             <property name="row_spacing">6</property>
                             <property name="column_spacing">12</property>
-                            <property name="n_rows">4</property>
                             <child>
                               <object class="GtkLabel" id="size_w_label">
                                 <property name="visible">True</property>
@@ -580,6 +1086,7 @@
                             <child>
                               <object class="GtkCheckButton" id="size_aspect_check">
                                 <property name="label" translatable="yes">Lock aspect ratio</property>
+                                <property name="use_action_appearance">False</property>
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">False</property>
@@ -597,6 +1104,7 @@
                             <child>
                               <object class="GtkButton" id="size_reset_image_button">
                                 <property name="label" translatable="yes">Reset image size</property>
+                                <property name="use_action_appearance">False</property>
                                 <property name="visible">True</property>
                                 <property name="can_focus">True</property>
                                 <property name="receives_default">True</property>
@@ -638,7 +1146,7 @@
                 </child>
               </object>
               <packing>
-                <property name="position">1</property>
+                <property name="position">2</property>
               </packing>
             </child>
             <child type="tab">
@@ -649,7 +1157,7 @@
                 <property name="label" translatable="yes">Position/Size</property>
               </object>
               <packing>
-                <property name="position">1</property>
+                <property name="position">2</property>
                 <property name="tab_fill">False</property>
               </packing>
             </child>
@@ -665,6 +1173,7 @@
                 <child>
                   <object class="GtkCheckButton" id="shadow_enable_check">
                     <property name="label" translatable="yes">Enable shadow</property>
+                    <property name="use_action_appearance">False</property>
                     <property name="visible">True</property>
                     <property name="can_focus">True</property>
                     <property name="receives_default">False</property>
@@ -691,7 +1200,6 @@
                         <property name="hexpand">True</property>
                         <property name="row_spacing">6</property>
                         <property name="column_spacing">12</property>
-                        <property name="n_rows">4</property>
                         <child>
                           <object class="GtkLabel" id="label40">
                             <property name="visible">True</property>
@@ -859,9 +1367,6 @@
                             <property name="height">1</property>
                           </packing>
                         </child>
-                        <child>
-                          <placeholder/>
-                        </child>
                       </object>
                     </child>
                   </object>
@@ -873,7 +1378,7 @@
                 </child>
               </object>
               <packing>
-                <property name="position">2</property>
+                <property name="position">3</property>
                 <property name="tab_fill">False</property>
               </packing>
             </child>
@@ -885,7 +1390,7 @@
                 <property name="label" translatable="yes">Shadow</property>
               </object>
               <packing>
-                <property name="position">2</property>
+                <property name="position">3</property>
                 <property name="tab_fill">False</property>
               </packing>
             </child>
@@ -899,6 +1404,28 @@
       </object>
     </child>
   </object>
+  <object class="GtkSizeGroup" id="color_box_sizegroup">
+    <widgets>
+      <widget name="shadow_color_box"/>
+      <widget name="line_color_box"/>
+      <widget name="fill_color_box"/>
+    </widgets>
+  </object>
+  <object class="GtkSizeGroup" id="label_sizegroup">
+    <widgets>
+      <widget name="line_w_label"/>
+      <widget name="line_color_label"/>
+      <widget name="fill_color_label"/>
+      <widget name="pos_x_label"/>
+      <widget name="pos_y_label"/>
+      <widget name="size_w_label"/>
+      <widget name="size_h_label"/>
+      <widget name="label40"/>
+      <widget name="label41"/>
+      <widget name="label45"/>
+      <widget name="label46"/>
+    </widgets>
+  </object>
   <object class="GtkSizeGroup" id="page_sizegroup">
     <property name="mode">both</property>
     <widgets>
@@ -907,6 +1434,16 @@
       <widget name="shadow_page_box"/>
     </widgets>
   </object>
+  <object class="GtkAdjustment" id="pos_x_adjustment">
+    <property name="upper">100</property>
+    <property name="step_increment">0.01</property>
+    <property name="page_increment">1</property>
+  </object>
+  <object class="GtkAdjustment" id="pos_y_adjustment">
+    <property name="upper">100</property>
+    <property name="step_increment">0.01</property>
+    <property name="page_increment">1</property>
+  </object>
   <object class="GtkSizeGroup" id="width_sizegroup">
     <widgets>
       <widget name="notebook"/>
diff --git a/glabels/Makefile.am b/glabels/Makefile.am
index da71fea..c0ff975 100644
--- a/glabels/Makefile.am
+++ b/glabels/Makefile.am
@@ -8,7 +8,6 @@ bin_PROGRAMS = glabels-4
 
 glabels_4_SOURCES = \
 	glabels.vala \
-	TMP_gdk_key.vala \
 	color.vala \
 	color_button.vala \
 	color_history.vala \
@@ -34,6 +33,7 @@ glabels_4_SOURCES = \
 	label.vala \
 	label_object.vala \
 	label_object_box.vala \
+	label_object_text.vala \
 	label_region.vala \
 	label_state.vala \
 	merge.vala \
@@ -50,13 +50,18 @@ glabels_4_SOURCES = \
 	mini_preview.vala \
 	new_label_dialog.vala \
 	object_editor.vala \
+	outline.vala \
 	prefs.vala \
 	print_op.vala \
 	print_op_dialog.vala \
 	property_editor.vala \
 	template_history.vala \
+	text_line.vala \
+	text_lines.vala \
+	text_node.vala \
 	ui.vala \
 	units_util.vala \
+	valign_type.vala \
 	view.vala \
 	window.vala \
 	xml_label.vala \
diff --git a/glabels/color_swatch.vala b/glabels/color_swatch.vala
index bc3387c..f53de14 100644
--- a/glabels/color_swatch.vala
+++ b/glabels/color_swatch.vala
@@ -101,7 +101,7 @@ namespace glabels
 				return;
 			}
 
-			unowned Cairo.Region region = window.get_clip_region();
+			Cairo.Region region = window.get_clip_region();
 			// redraw the cairo canvas completely by exposing it
 			window.invalidate_region(region, true);
 			window.process_updates(true);
diff --git a/glabels/font_sample.vala b/glabels/font_sample.vala
index 508e338..e989c91 100644
--- a/glabels/font_sample.vala
+++ b/glabels/font_sample.vala
@@ -128,7 +128,7 @@ namespace glabels
 				return;
 			}
 
-			unowned Cairo.Region region = window.get_clip_region ();
+			Cairo.Region region = window.get_clip_region ();
 			// redraw the cairo canvas completely by exposing it
 			window.invalidate_region (region, true);
 			window.process_updates (true);
diff --git a/glabels/handle.vala b/glabels/handle.vala
index 7c8b060..e65b421 100644
--- a/glabels/handle.vala
+++ b/glabels/handle.vala
@@ -1,4 +1,4 @@
-/*  label_object_box.vala
+/*  handle.vala
  *
  *  Copyright (C) 2011  Jim Evins <evins snaught com>
  *
diff --git a/glabels/label_object.vala b/glabels/label_object.vala
index 79fd089..687fba1 100644
--- a/glabels/label_object.vala
+++ b/glabels/label_object.vala
@@ -32,10 +32,12 @@ namespace glabels
 
 		private   bool         selected;
 		protected List<Handle> handles;
+		protected Outline?     outline;
 
 		private   double       aspect_ratio;
 
 
+
 		/**
 		 * Parent label
 		 */
@@ -254,6 +256,25 @@ namespace glabels
 
 
 		/**
+		 * Text vertical alignment
+		 */
+		public ValignType text_valignment
+		{
+			get { return _text_valignment; }
+
+			set
+			{
+				if ( _text_valignment != value )
+				{
+					_text_valignment = value;
+					changed();
+				}
+			}
+		}
+		private ValignType _text_valignment;
+
+
+		/**
 		 * Text line spacing
 		 */
 		public double text_line_spacing
@@ -452,6 +473,7 @@ namespace glabels
 			_font_italic_flag        = prefs.default_font_italic_flag;
 			_text_color_node         = ColorNode.from_color( prefs.default_text_color );
 			_text_alignment          = prefs.default_text_alignment;
+			_text_valignment         = ValignType.TOP;
 			_text_line_spacing       = prefs.default_text_line_spacing;
 
 			_line_width              = prefs.default_line_width;
@@ -700,6 +722,11 @@ namespace glabels
 			cr.translate( x0, y0 );
 			cr.transform( matrix );
 
+			if ( outline != null )
+			{
+				outline.draw( cr );
+			}
+
 			foreach( Handle handle in handles )
 			{
 				handle.draw( cr );
@@ -723,6 +750,14 @@ namespace glabels
 
 			bool ret_val = is_object_located_at( cr, x, y );
 
+			if ( (outline != null) && is_selected() )
+			{
+				if ( outline.in_stroke( cr, x, y ) )
+				{
+					ret_val = true;
+				}
+			}
+
 			cr.restore();
 
 			return ret_val;
diff --git a/glabels/label_object_text.vala b/glabels/label_object_text.vala
new file mode 100644
index 0000000..4cb0146
--- /dev/null
+++ b/glabels/label_object_text.vala
@@ -0,0 +1,293 @@
+/*  label_object_text.vala
+ *
+ *  Copyright (C) 2012  Jim Evins <evins snaught com>
+ *
+ *  This file is part of gLabels.
+ *
+ *  gLabels is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  gLabels 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 gLabels.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+	public class LabelObjectText : LabelObject
+	{
+		public  Gtk.TextBuffer   buffer;
+
+		private Gtk.TextTagTable tag_table;
+		private bool             size_changed;
+
+		private const double FONT_SCALE = (72.0/96.0);
+		private const double SELECTION_SLOP_PIXELS = 4.0;
+		private const double TEXT_MARGIN = 3.0;
+
+		public bool auto_shrink { get; set; default = false; }
+
+
+		public LabelObjectText()
+		{
+			handles.append( new HandleSouthEast( this ) );
+			handles.append( new HandleSouthWest( this ) );
+			handles.append( new HandleNorthEast( this ) );
+			handles.append( new HandleNorthWest( this ) );
+			handles.append( new HandleEast( this ) );
+			handles.append( new HandleSouth( this ) );
+			handles.append( new HandleWest( this ) );
+			handles.append( new HandleNorth( this ) );
+
+			outline = new Outline( this );
+
+			tag_table    = new Gtk.TextTagTable();
+			buffer       = new Gtk.TextBuffer( tag_table );
+			size_changed = true;
+
+			buffer.begin_user_action.connect( on_buffer_begin_user_action );
+			buffer.changed.connect( on_buffer_changed );
+		}
+
+
+		public override bool can_text()
+		{
+			return true;
+		}
+
+
+		public override LabelObject dup()
+		{
+			LabelObjectText copy = new LabelObjectText();
+
+			copy.set_common_properties_from_object( this );
+
+			return copy;
+		}
+
+
+		public override void draw_object( Cairo.Context cr, bool in_editor, MergeRecord? record )
+		{
+			Color text_color = text_color_node.expand( record );
+
+			if ( in_editor && text_color_node.field_flag )
+			{
+				text_color = Color.from_rgba( 0, 0, 0, 0.5 );
+			}
+
+			draw_text_real( cr, in_editor, record, text_color );
+		}
+
+
+		public override void draw_shadow( Cairo.Context cr, bool in_editor, MergeRecord? record )
+		{
+			Color shadow_color = shadow_color_node.expand( record );
+
+			if ( in_editor && shadow_color_node.field_flag )
+			{
+				shadow_color = Color.black();
+			}
+
+			shadow_color.set_opacity( shadow_opacity );
+
+			draw_text_real( cr, in_editor, record, shadow_color );
+		}
+
+
+		private void draw_text_real( Cairo.Context cr, bool in_editor, MergeRecord? record, Color color )
+		{
+			set_text_path( cr, in_editor, record);
+
+			cr.set_source_rgba( color.r, color.g, color.b, color.a );
+			cr.fill();
+		}
+
+
+		public override bool is_object_located_at( Cairo.Context cr, double x, double y )
+		{
+			if ( (x >= 0) && (x <= w) && (y >=0) && (y <= h) )
+			{
+				cr.new_path();
+				set_text_path( cr, true, null);
+				if ( cr.in_fill( x, y ) )
+				{
+					return true;
+				}
+
+				double scale_x = 1.0;
+				double scale_y = 1.0;
+				cr.device_to_user_distance( ref scale_x, ref scale_y );
+				cr.set_line_width( 2*SELECTION_SLOP_PIXELS*scale_x );
+				if (cr.in_stroke( x, y ))
+				{
+					return true;
+				}
+			}
+
+			return false;
+		}
+
+
+		private void set_text_path( Cairo.Context cr, bool in_editor, MergeRecord? record )
+		{
+			cr.save();
+
+			TextLines lines = get_lines();
+			string    text = lines.expand( record );
+
+			Pango.Style style = font_italic_flag ? Pango.Style.ITALIC : Pango.Style.NORMAL;
+
+			double scaled_font_size   = font_size * FONT_SCALE;
+
+			if (!in_editor && (record != null) && auto_shrink)
+			{
+				scaled_font_size = auto_shrink_font_size( cr,
+				                                          font_family, scaled_font_size, font_weight, style,
+				                                          text, w, h );
+			}
+
+
+			Pango.Layout layout = Pango.cairo_create_layout( cr );
+
+			Cairo.FontOptions font_options = new Cairo.FontOptions();
+			font_options.set_hint_metrics( Cairo.HintMetrics.OFF );
+			Pango.Context context = layout.get_context();
+			Pango.cairo_context_set_font_options( context, font_options );
+
+			Pango.FontDescription desc = new Pango.FontDescription();
+			desc.set_family( font_family );
+			desc.set_weight( font_weight );
+			desc.set_size( (int)(scaled_font_size * Pango.SCALE + 0.5) );
+			desc.set_style( style );
+			layout.set_font_description( desc );
+
+			layout.set_text( text, -1 );
+			layout.set_spacing( (int)(scaled_font_size * (text_line_spacing-1) * Pango.SCALE + 0.5) );
+			layout.set_width( (int)(w * Pango.SCALE + 0.5) );
+			layout.set_wrap( Pango.WrapMode.WORD );
+			layout.set_alignment( text_alignment );
+
+			int iw, ih;
+			layout.get_pixel_size( out iw, out ih );
+
+			double y;
+			switch (text_valignment)
+			{
+			case ValignType.CENTER:
+				y = (h - ih) / 2;
+				break;
+			case ValignType.BOTTOM:
+				y = h - ih;
+				break;
+			default:
+				y = 0;
+				break;
+			}
+
+			cr.move_to( TEXT_MARGIN, y );
+			Pango.cairo_layout_path( cr, layout );
+
+			cr.restore();
+		}
+
+
+		private double auto_shrink_font_size( Cairo.Context cr,
+		                                      string        family,
+		                                      double        size,
+		                                      Pango.Weight  weight,
+		                                      Pango.Style   style,
+		                                      string        text,
+		                                      double        width,
+		                                      double        height )
+		{
+			Pango.Layout layout = Pango.cairo_create_layout( cr );
+
+			Pango.FontDescription desc = new Pango.FontDescription();
+			desc.set_family( family );
+			desc.set_weight( weight );
+			desc.set_style( style );
+			desc.set_size( (int)(size * Pango.SCALE + 0.5) );
+			layout.set_font_description( desc );
+
+			layout.set_text( text, -1 );
+			layout.set_spacing( (int)(size * (text_line_spacing-1) * Pango.SCALE + 0.5) );
+			layout.set_width( -1 );
+
+			int iw, ih;
+			layout.get_size( out iw, out ih );
+			double layout_width  = iw / (double)Pango.SCALE;
+			double layout_height = ih / (double)Pango.SCALE;
+
+			double new_wsize = size;
+			double new_hsize = size;
+			if ( layout_width > width )
+			{
+				/* Scale down. */
+				new_wsize = size * (width-2*TEXT_MARGIN) / layout_width;
+
+				/* Round down to nearest 1/2 point */
+				new_wsize = (int)(new_wsize*2.0) / 2.0;
+
+				/* don't get ridiculously small. */
+				if (new_wsize < 1.0)
+				{
+					new_wsize = 1.0;
+				}
+			}
+
+			if ( layout_height > height )
+			{
+				/* Scale down. */
+				new_hsize = size * height / layout_height;
+
+				/* Round down to nearest 1/2 point */
+				new_hsize = (int)(new_hsize*2.0) / 2.0;
+
+				/* don't get ridiculously small. */
+				if (new_hsize < 1.0)
+				{
+					new_hsize = 1.0;
+				}
+			}
+
+			return (new_wsize < new_hsize ? new_wsize : new_hsize);
+		}
+
+
+		private void on_buffer_begin_user_action()
+		{
+		}
+
+
+		private void on_buffer_changed()
+		{
+			size_changed = true;
+
+			changed();
+		}
+
+
+		private TextLines get_lines()
+		{
+			Gtk.TextIter start, end;
+
+			buffer.get_bounds( out start, out end );
+			string text = buffer.get_text( start, end, false );
+			TextLines lines = new TextLines( text );
+
+			return lines;
+		}
+
+	}
+
+}
diff --git a/glabels/mini_preview.vala b/glabels/mini_preview.vala
index 154b2d1..d5d51dd 100644
--- a/glabels/mini_preview.vala
+++ b/glabels/mini_preview.vala
@@ -232,7 +232,7 @@ namespace glabels
 			{
 				update_scheduled_flag = true;
 
-				unowned Cairo.Region region = window.get_clip_region ();
+				Cairo.Region region = window.get_clip_region ();
 				// redraw the cairo canvas completely by exposing it
 				window.invalidate_region (region, true);
 			}
diff --git a/glabels/object_editor.vala b/glabels/object_editor.vala
index 7236c3c..7efcadb 100644
--- a/glabels/object_editor.vala
+++ b/glabels/object_editor.vala
@@ -27,49 +27,69 @@ namespace glabels
 
 	class ObjectEditor : Gtk.Box
 	{
-		private Prefs           prefs;
-		private Units           units;
-
-		private Model           model;
-		private LabelObject?    object;
-
-
-		private Gtk.Image       title_image;
-		private Gtk.Label       title_label;
-		private Gtk.Notebook    notebook;
-
-		private Gtk.Box         line_fill_page_box;
-		private Gtk.Box         pos_size_page_box;
-		private Gtk.Box         shadow_page_box;
-
-		private Gtk.SpinButton  line_width_spin;
-		private Gtk.Box         line_color_box;
-		private ColorButton     line_color_button;
-
-		private Gtk.Box         fill_color_box;
-		private ColorButton     fill_color_button;
-
-		private Gtk.SpinButton  pos_x_spin;
-		private Gtk.SpinButton  pos_y_spin;
-		private Gtk.Label       pos_x_units_label;
-		private Gtk.Label       pos_y_units_label;
-
-		private Gtk.SpinButton  size_w_spin;
-		private Gtk.SpinButton  size_h_spin;
-		private Gtk.Label       size_w_units_label;
-		private Gtk.Label       size_h_units_label;
-		private Gtk.CheckButton size_aspect_check;
-		private Gtk.Button      size_reset_image_button;
-
-		private Gtk.CheckButton shadow_enable_check;
-		private Gtk.Grid        shadow_controls_grid;
-		private Gtk.SpinButton  shadow_x_spin;
-		private Gtk.SpinButton  shadow_y_spin;
-		private Gtk.Label       shadow_x_units_label;
-		private Gtk.Label       shadow_y_units_label;
-		private Gtk.Box         shadow_color_box;
-		private ColorButton     shadow_color_button;
-		private Gtk.SpinButton  shadow_opacity_spin;
+		private Prefs            prefs;
+		private Units            units;
+
+		private Model            model;
+		private LabelObject?     object;
+
+
+		private Gtk.Image        title_image;
+		private Gtk.Label        title_label;
+		private Gtk.Notebook     notebook;
+
+		private Gtk.Box          text_page_box;
+		private Gtk.Box          line_fill_page_box;
+		private Gtk.Box          pos_size_page_box;
+		private Gtk.Box          shadow_page_box;
+
+		private Gtk.Box          text_font_family_box;
+		private FontButton       text_font_family_button;
+		private Gtk.SpinButton   text_font_size_spin;
+		private Gtk.ToggleButton text_font_bold_toggle;
+		private Gtk.ToggleButton text_font_italic_toggle;
+		private Gtk.ToggleButton text_font_underline_toggle;
+		private Gtk.Box          text_color_box;
+		private ColorButton      text_color_button;
+		private Gtk.ToggleButton text_align_left_toggle;
+		private Gtk.ToggleButton text_align_center_toggle;
+		private Gtk.ToggleButton text_align_right_toggle;
+		private Gtk.ToggleButton text_align_top_toggle;
+		private Gtk.ToggleButton text_align_middle_toggle;
+		private Gtk.ToggleButton text_align_bottom_toggle;
+		private Gtk.SpinButton   text_line_spacing_spin;
+		private Gtk.TextView     text_text_view;
+		private Gtk.Box          text_insert_field_box;
+		private FieldButton      text_insert_field_button;
+
+		private Gtk.SpinButton   line_width_spin;
+		private Gtk.Box          line_color_box;
+		private ColorButton      line_color_button;
+
+		private Gtk.Box          fill_color_box;
+		private ColorButton      fill_color_button;
+
+		private Gtk.SpinButton   pos_x_spin;
+		private Gtk.SpinButton   pos_y_spin;
+		private Gtk.Label        pos_x_units_label;
+		private Gtk.Label        pos_y_units_label;
+
+		private Gtk.SpinButton   size_w_spin;
+		private Gtk.SpinButton   size_h_spin;
+		private Gtk.Label        size_w_units_label;
+		private Gtk.Label        size_h_units_label;
+		private Gtk.CheckButton  size_aspect_check;
+		private Gtk.Button       size_reset_image_button;
+
+		private Gtk.CheckButton  shadow_enable_check;
+		private Gtk.Grid         shadow_controls_grid;
+		private Gtk.SpinButton   shadow_x_spin;
+		private Gtk.SpinButton   shadow_y_spin;
+		private Gtk.Label        shadow_x_units_label;
+		private Gtk.Label        shadow_y_units_label;
+		private Gtk.Box          shadow_color_box;
+		private ColorButton      shadow_color_button;
+		private Gtk.SpinButton   shadow_opacity_spin;
 
 
 		public ObjectEditor()
@@ -108,11 +128,44 @@ namespace glabels
 			title_label.set_sensitive( false );
 
 			/* Notebook pages. */
+			text_page_box      = builder.get_object( "text_page_box" )      as Gtk.Box;
 			line_fill_page_box = builder.get_object( "line_fill_page_box" ) as Gtk.Box;
 			pos_size_page_box  = builder.get_object( "pos_size_page_box" )  as Gtk.Box;
 			shadow_page_box    = builder.get_object( "shadow_page_box" )    as Gtk.Box;
 
 
+			/* Text widgets. */
+			text_font_family_box       = builder.get_object( "text_font_family_box" )       as Gtk.Box;
+			text_font_size_spin        = builder.get_object( "text_font_size_spin" )        as Gtk.SpinButton;
+			text_font_bold_toggle      = builder.get_object( "text_font_bold_toggle" )      as Gtk.ToggleButton;
+			text_font_italic_toggle    = builder.get_object( "text_font_italic_toggle" )    as Gtk.ToggleButton;
+			text_font_underline_toggle = builder.get_object( "text_font_underline_toggle" ) as Gtk.ToggleButton;
+			text_color_box             = builder.get_object( "text_color_box" )             as Gtk.Box;
+			text_align_left_toggle     = builder.get_object( "text_align_left_toggle" )     as Gtk.ToggleButton;
+			text_align_center_toggle   = builder.get_object( "text_align_center_toggle" )   as Gtk.ToggleButton;
+			text_align_right_toggle    = builder.get_object( "text_align_right_toggle" )    as Gtk.ToggleButton;
+			text_align_top_toggle      = builder.get_object( "text_align_top_toggle" )      as Gtk.ToggleButton;
+			text_align_middle_toggle   = builder.get_object( "text_align_middle_toggle" )   as Gtk.ToggleButton;
+			text_align_bottom_toggle   = builder.get_object( "text_align_bottom_toggle" )   as Gtk.ToggleButton;
+			text_line_spacing_spin     = builder.get_object( "text_line_spacing_spin" )     as Gtk.SpinButton;
+			text_text_view             = builder.get_object( "text_text_view" )             as Gtk.TextView;
+			text_insert_field_box      = builder.get_object( "text_insert_field_box" )      as Gtk.Box;
+
+			text_font_family_button = new FontButton( null );
+			text_font_family_box.pack_start( text_font_family_button, false, false, 0 );
+
+			text_color_button = new ColorButton( _("Default"), Color.black(), Color.black() );
+			text_color_box.pack_start( text_color_button, true, true, 0 );
+
+			text_insert_field_button = new FieldButton( null );
+			text_insert_field_box.pack_start( text_insert_field_button, true, true, 0 );
+/*
+			text_font_family_button.changed.connect( on_text_font_family_button_changed );
+			text_font_size_spin.value_changed.connect( on_text_font_size_spin_changed );
+			text_color_button.color_changed.connect( on_text_color_button_changed );
+			text_insert_field_button.changed.connect( on_text_field_button_changed );
+*/
+
 			/* Line widgets. */
 			line_width_spin         = builder.get_object( "line_width_spin" )         as Gtk.SpinButton;
 			line_color_box          = builder.get_object( "line_color_box" )          as Gtk.Box;
@@ -248,11 +301,28 @@ namespace glabels
 			{
 				object = model.label.get_1st_selected_object();
 
-				if ( object is LabelObjectBox  )
+				if ( object is LabelObjectText  )
+				{
+					LabelObjectText tobject = object as LabelObjectText;
+
+					title_image.set_from_icon_name( "glabels-box", Gtk.IconSize.LARGE_TOOLBAR );
+					title_label.set_text( "<b>%s</b>".printf( _("Text object properties") ) );
+
+					text_text_view.set_buffer( tobject.buffer );
+
+					text_page_box.show_all();
+					line_fill_page_box.hide();
+					pos_size_page_box.show_all();
+					shadow_page_box.show_all();
+
+					size_reset_image_button.hide();
+				}
+				else if ( object is LabelObjectBox  )
 				{
 					title_image.set_from_icon_name( "glabels-box", Gtk.IconSize.LARGE_TOOLBAR );
 					title_label.set_text( "<b>%s</b>".printf( _("Box object properties") ) );
 
+					text_page_box.hide();
 					line_fill_page_box.show_all();
 					pos_size_page_box.show_all();
 					shadow_page_box.show_all();
diff --git a/glabels/outline.vala b/glabels/outline.vala
new file mode 100644
index 0000000..c640b0a
--- /dev/null
+++ b/glabels/outline.vala
@@ -0,0 +1,85 @@
+/*  outline.vala
+ *
+ *  Copyright (C) 2011  Jim Evins <evins snaught com>
+ *
+ *  This file is part of gLabels.
+ *
+ *  gLabels is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  gLabels 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 gLabels.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+	private const double OUTLINE_WIDTH_PIXELS = 2;
+	private const double SELECTION_SLOP_PIXELS = 4;
+
+	private const Color OUTLINE_COLOR = { 0.0,  0.0,   0.0,  0.8 };
+
+
+	public class Outline
+	{
+		public weak LabelObject owner { get; protected set; }
+
+
+		public Outline( LabelObject owner )
+		{
+			this.owner = owner;
+		}
+
+
+		public void draw( Cairo.Context cr )
+		{
+			double dashes[2] = { 2, 2 };
+
+			cr.save();
+
+			cr.rectangle( 0, 0, owner.w, owner.h );
+
+			double scale_x = 1.0;
+			double scale_y = 1.0;
+			cr.device_to_user_distance( ref scale_x, ref scale_y );
+			cr.scale( scale_x, scale_y );
+
+			cr.set_dash( dashes, 0 );
+			cr.set_line_width( OUTLINE_WIDTH_PIXELS );
+			cr.set_source_rgba( OUTLINE_COLOR.r, OUTLINE_COLOR.g, OUTLINE_COLOR.b,
+			                    OUTLINE_COLOR.a );
+			cr.stroke();
+
+			cr.restore();
+		}
+
+
+		public bool in_stroke( Cairo.Context cr, double x, double y )
+		{
+			cr.rectangle( 0, 0, owner.w, owner.h );
+
+			double scale_x = 1.0;
+			double scale_y = 1.0;
+			cr.device_to_user_distance( ref scale_x, ref scale_y );
+
+			cr.set_line_width( 2*SELECTION_SLOP_PIXELS*scale_x );
+
+			return cr.in_stroke( x, y );
+		}
+
+
+	}
+
+
+}
+
diff --git a/glabels/text_line.vala b/glabels/text_line.vala
new file mode 100644
index 0000000..8eb057d
--- /dev/null
+++ b/glabels/text_line.vala
@@ -0,0 +1,80 @@
+/*  text_line.vala
+ *
+ *  Copyright (C) 2012  Jim Evins <evins snaught com>
+ *
+ *  This file is part of gLabels.
+ *
+ *  gLabels is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  gLabels 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 gLabels.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+	public class TextLine
+	{
+		public unowned List<TextNode> nodes { get; private set; }
+
+
+		public TextLine( string text, int i_start, out int i_next )
+		{
+			i_next = i_start;
+
+			for ( int i = i_start; text[i] != 0; i = i_next )
+			{
+				if ( text[i] != '\n' )
+				{
+					TextNode node = new TextNode.parse( text, i, out i_next );
+					nodes.append( node );
+				}
+				else
+				{
+					i_next++;
+					break;
+				}
+			}
+		}
+
+
+		public void expand( MergeRecord? record, ref StringBuilder builder )
+		{
+			/* special case: something like ${ADDRESS2} = "" on line by itself. */
+			/*               in such circumstances ignore the line completely.  */
+			if ( nodes.first().next == null )
+			{
+				if ( nodes.first().data.is_empty_field( record ) )
+				{
+					return;
+				}
+			}
+
+			/* prepend newline unless this is the first line. */
+			if ( builder.len > 0 )
+			{
+				builder.append_c( '\n' );
+			}
+
+			/* expand each node */
+			foreach (TextNode node in nodes)
+			{
+				builder.append( node.expand( record ) );
+			}
+		}
+
+
+	}
+
+}
diff --git a/glabels/text_lines.vala b/glabels/text_lines.vala
new file mode 100644
index 0000000..18149ed
--- /dev/null
+++ b/glabels/text_lines.vala
@@ -0,0 +1,60 @@
+/*  text_lines.vala
+ *
+ *  Copyright (C) 2012  Jim Evins <evins snaught com>
+ *
+ *  This file is part of gLabels.
+ *
+ *  gLabels is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  gLabels 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 gLabels.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+	public class TextLines
+	{
+		public unowned List<TextLine> lines { get; private set; }
+
+
+		public TextLines( string text )
+		{
+			int i_next  = 0;
+
+			for ( int i = 0; text[i] != 0; i = i_next )
+			{
+				stderr.printf( "Text[%d] = %c, ", i, text[i] ); 
+				lines.append( new TextLine( text, i, out i_next ) );
+				stderr.printf( "i_next =%d\n", i_next ); 
+			}
+		}
+
+
+		public string expand( MergeRecord? record )
+		{
+			StringBuilder builder = new StringBuilder();
+
+			foreach ( TextLine line in lines )
+			{
+				line.expand( record, ref builder );
+			}
+
+			return builder.str;
+		}
+
+
+	}
+
+}
diff --git a/glabels/text_node.vala b/glabels/text_node.vala
new file mode 100644
index 0000000..a7d2ca5
--- /dev/null
+++ b/glabels/text_node.vala
@@ -0,0 +1,264 @@
+/*  text_node.vala
+ *
+ *  Copyright (C) 2012  Jim Evins <evins snaught com>
+ *
+ *  This file is part of gLabels.
+ *
+ *  gLabels is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  gLabels 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 gLabels.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+
+	public class TextNode
+	{
+		public  bool   field_flag { get; private set; }
+		public  string data       { get; private set; }
+
+
+		public TextNode( bool field_flag, string data )
+		{
+			this.field_flag = field_flag;
+			this.data       = data;
+		}
+
+
+		public TextNode.dup( TextNode node )
+		{
+			this( node.field_flag, node.data );
+		}
+
+
+		private enum State { START,
+		                     LITERAL, LITERAL_DOLLAR,
+		                     START_DOLLAR, FIELD,
+		                     DONE }
+
+
+		public TextNode.parse( string text, int i_start, out int i_next )
+		{
+			State         state        = State.START;
+			StringBuilder literal_text = new StringBuilder();
+			StringBuilder field_name   = new StringBuilder();
+			bool          field_flag   = false;
+
+			int i = i_start;
+
+			while ( state != State.DONE )
+			{
+				char c = text[i];
+
+				switch (state) {
+
+				case State.START:
+					switch (c) {
+					case '$':
+						/* May be start of a field node. */
+						i++;
+						state = State.START_DOLLAR;
+						break;
+					case '\n':
+						state = State.DONE;
+						break;
+					case 0:
+						state = State.DONE;
+						break;
+					default:
+						/* Start a literal text node. */
+						literal_text.append_c( c );
+						i++;
+						state = State.LITERAL;
+						break;
+					}
+					break;
+
+				case State.LITERAL:
+					switch (c) {
+					case '$':
+						/* May be the beginning of a field node. */
+						i++;
+						state = State.LITERAL_DOLLAR;
+						break;
+					case '\n':
+						state = State.DONE;
+						break;
+					case 0:
+						state = State.DONE;
+						break;
+					default:
+						literal_text.append_c( c );
+						i++;
+						state = State.LITERAL;
+						break;
+					}
+					break;
+
+				case State.LITERAL_DOLLAR:
+					switch (c) {
+					case '{':
+						/* "${" indicates the start of a new field node, gather for literal too. */
+						literal_text.append_c( '$' );
+						i++;
+						state = State.DONE;
+						break;
+					case '\n':
+						/* Append "$" to literal text, don't gather newline. */
+						literal_text.append_c( '$' );
+						i++;
+						state = State.DONE;
+						break;
+					case 0:
+						/* Append "$" to literal text, don't gather null. */
+						literal_text.append_c( '$' );
+						i++;
+						state = State.DONE;
+						break;
+					default:
+						/* Append "$" to literal text, gather this character too. */
+						literal_text.append_c( '$' );
+						literal_text.append_c( c );
+						i+=2;
+						state = State.LITERAL;
+						break;
+					}
+					break;
+
+				case State.START_DOLLAR:
+					switch (c) {
+					case '{':
+						/* This is probably the begging of a field node, gather for literal too. */
+						literal_text.append_c( c );
+						i++;
+						state = State.FIELD;
+						break;
+					case '\n':
+						state = State.DONE;
+						break;
+					case 0:
+						state = State.DONE;
+						break;
+					default:
+						/* The "$" was literal. */
+						literal_text.append_c( c );
+						i++;
+						state = State.LITERAL;
+						break;
+					}
+					break;
+
+				case State.FIELD:
+					switch (c) {
+					case '}':
+						/* We now finally know that this node is really field node. */
+						field_flag = true;
+						i++;
+						state = State.DONE;
+						break;
+					case '\n':
+						state = State.DONE;
+						break;
+					case 0:
+						state = State.DONE;
+						break;
+					default:
+						/* Gather for field name and literal, just in case. */
+						field_name.append_unichar( c );
+						literal_text.append_c( c );
+						i++;
+						state = State.FIELD;
+						break;
+					}
+					break;
+
+				}
+
+			}
+
+			string data;
+			if ( field_flag )
+			{
+				data = field_name.str;
+			}
+			else
+			{
+				data = literal_text.str;
+			}
+
+			this( field_flag, data );
+
+			i_next = i;
+		}
+
+
+		public bool equal( TextNode node2 )
+		{
+			if ( this.field_flag != node2.field_flag )
+			{
+				return false;
+			}
+
+			return ( this.data == node2.data );
+		}
+
+
+		public string expand( MergeRecord? record )
+		{
+			if ( field_flag )
+			{
+
+				if ( record == null )
+				{
+					return "${%s}".printf( data );
+				}
+				else
+				{
+					string? text = record.eval_key( data );
+					if ( text != null )
+					{
+						return text;
+					}
+					else
+					{
+						return "";
+					}
+				}
+
+			}
+			else
+			{
+				return data;
+			}
+		}
+
+
+		public bool is_empty_field( MergeRecord? record )
+		{
+			if ( (record !=null) && field_flag )
+			{
+				string? text = record.eval_key( data );
+				return ( (text == null) || (text == "") );
+			}
+			else
+			{
+				return false;
+			}
+		}
+
+
+	}
+
+}
diff --git a/glabels/ui.vala b/glabels/ui.vala
index ffa31fd..d2037a2 100644
--- a/glabels/ui.vala
+++ b/glabels/ui.vala
@@ -1152,12 +1152,7 @@ namespace glabels
 
 		private void on_objects_create_text( Gtk.Action action )
 		{
-			/*
-			if (window->view != NULL) {
-				gl_view_object_create_mode (GL_VIEW(window->view),
-				                            GL_LABEL_OBJECT_TEXT);
-			}
-			*/
+			window.view.create_text_mode();
 		}
 
 
diff --git a/glabels/valign_type.vala b/glabels/valign_type.vala
new file mode 100644
index 0000000..d260f8c
--- /dev/null
+++ b/glabels/valign_type.vala
@@ -0,0 +1,28 @@
+/*  valign_type.vala
+ *
+ *  Copyright (C) 2012  Jim Evins <evins snaught com>
+ *
+ *  This file is part of gLabels.
+ *
+ *  gLabels is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  gLabels 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 gLabels.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+using GLib;
+
+namespace glabels
+{
+	public enum ValignType { TOP, CENTER, BOTTOM }
+}
+
diff --git a/glabels/view.vala b/glabels/view.vala
index 8dc2b96..c4505ef 100644
--- a/glabels/view.vala
+++ b/glabels/view.vala
@@ -385,6 +385,28 @@ namespace glabels
 		}
 
 
+		public void create_text_mode()
+		{
+			Gdk.Window window = canvas.get_window();
+
+			try
+			{
+				Gdk.Pixbuf pixbuf = Gdk.Pixbuf.from_pixdata( Cursor.text_pixdata, false );
+				Gdk.Cursor cursor = new Gdk.Cursor.from_pixbuf( Gdk.Display.get_default(),
+				                                                pixbuf, CURSOR_X_HOTSPOT, CURSOR_Y_HOTSPOT );
+				window.set_cursor( cursor );
+			}
+			catch ( Error err )
+			{
+				error( "%s\n", err.message );
+			}
+
+			in_object_create_mode = true;
+			create_object_type    = CreateType.TEXT;
+			state                 = State.IDLE;
+		}
+
+
 		private void on_prefs_changed()
 		{
 			grid_spacing = UnitsUtil.get_grid_size( prefs.units );
@@ -844,7 +866,9 @@ namespace glabels
 						/* TODO */
 						break;
 					case CreateType.TEXT:
-						/* TODO */
+						create_object.set_position( double.min( x, create_x0 ), double.min( y, create_y0 ) );
+						create_object.set_size( double.max( x, create_x0 ) - double.min( x, create_x0 ),
+						                        double.max( y, create_y0 ) - double.min( y, create_y0 ) );
 						break;
 					case CreateType.BARCODE:
 						/* TODO */
@@ -970,7 +994,7 @@ namespace glabels
 							/* TODO */
 							break;
 						case CreateType.TEXT:
-							/* TODO */
+							create_object = new LabelObjectText() as LabelObject;
 							break;
 						case CreateType.BARCODE:
 							/* TODO */
diff --git a/glabels/xml_label.vala b/glabels/xml_label.vala
index 19d7dac..fee45ab 100644
--- a/glabels/xml_label.vala
+++ b/glabels/xml_label.vala
@@ -186,7 +186,7 @@ namespace glabels
 				{
 
 				case "Object-text":
-					/* TODO. */
+					parse_object_text_node( child, label );
 					break;
 
 				case "Object-box":
@@ -263,6 +263,32 @@ namespace glabels
 		}
 
 
+		private void parse_object_text_node( Xml.Node node,
+		                                     Label    label )
+		{
+			LabelObjectText object = new LabelObjectText();
+
+		
+			/* position attrs */
+			object.x0 = XmlUtil.get_prop_length( node, "x", 0.0 );
+			object.y0 = XmlUtil.get_prop_length( node, "y", 0.0 );
+
+			/* size attrs */
+			object.w = XmlUtil.get_prop_length( node, "w", 0 );
+			object.h = XmlUtil.get_prop_length( node, "h", 0 );
+
+			/* affine attrs */
+			parse_affine_attrs( node, object );
+
+			/* shadow attrs */
+			parse_shadow_attrs( node, object );
+
+			// TODO: parse contents.
+
+			label.add_object( object );
+		}
+
+
 		private void parse_affine_attrs( Xml.Node    node,
 		                                 LabelObject object )
 		{
@@ -389,6 +415,10 @@ namespace glabels
 				{
 					create_object_box_node( node, ns, (LabelObjectBox)object );
 				}
+				else if ( object is LabelObjectText )
+				{
+					create_object_text_node( node, ns, (LabelObjectText)object );
+				}
 				else /* TODO: other object types. */
 				{
 					message( "Unknown label object." );
@@ -440,6 +470,30 @@ namespace glabels
 		}
 
 
+		private void create_object_text_node( Xml.Node        parent,
+		                                      Xml.Ns          ns,
+		                                      LabelObjectText object )
+		{
+			unowned Xml.Node *node = parent.new_child( ns, "Object-text" );
+
+			/* position attrs */
+			XmlUtil.set_prop_length( node, "x", object.x0 );
+			XmlUtil.set_prop_length( node, "y", object.y0 );
+
+			/* size attrs */
+			XmlUtil.set_prop_length( node, "w", object.w );
+			XmlUtil.set_prop_length( node, "h", object.h );
+
+			/* affine attrs */
+			create_affine_attrs( node, object );
+
+			/* shadow attrs */
+			create_shadow_attrs( node, object );
+
+			// TODO: create contents.
+		}
+
+
 		private void create_affine_attrs( Xml.Node    node,
 		                                  LabelObject object )
 		{



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]