[pitivi] Implement a title editor



commit 459532f4252d547f91427af517c35d0e3200295b
Author: Matas Brazdeikis <matas brazdeikis lt>
Date:   Tue Aug 14 08:49:39 2012 +0100

    Implement a title editor

 data/ui/Makefile.am      |    1 +
 data/ui/titleeditor.ui   |  540 ++++++++++++++++++++++++++++++++
 pitivi/Makefile.am       |    1 +
 pitivi/mainwindow.py     |    7 +-
 pitivi/timeline/track.py |   67 ++++-
 pitivi/titleeditor.py    |  760 ++++++++++++++++++++++++++++++++++++++++++++++
 pitivi/utils/timeline.py |    4 +-
 pitivi/viewer.py         |    3 +-
 8 files changed, 1378 insertions(+), 5 deletions(-)
---
diff --git a/data/ui/Makefile.am b/data/ui/Makefile.am
index 67273fd..6aae056 100644
--- a/data/ui/Makefile.am
+++ b/data/ui/Makefile.am
@@ -12,6 +12,7 @@ ui_DATA = \
 	cliptransformation.ui \
 	startupwizard.ui \
 	clipmediaprops.ui \
+	titleeditor.ui \
 	$(NULL)
 
 EXTRA_DIST = \
diff --git a/data/ui/titleeditor.ui b/data/ui/titleeditor.ui
new file mode 100644
index 0000000..84e4e58
--- /dev/null
+++ b/data/ui/titleeditor.ui
@@ -0,0 +1,540 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <object class="GtkVBox" id="box1">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <child>
+      <object class="GtkInfoBar" id="infobar1">
+        <property name="visible">True</property>
+        <property name="app_paintable">True</property>
+        <property name="can_focus">False</property>
+        <child internal-child="content_area">
+          <object class="GtkBox" id="infobar-content_area1">
+            <property name="can_focus">False</property>
+            <property name="border_width">8</property>
+            <property name="spacing">16</property>
+            <child>
+              <object class="GtkLabel" id="info_bar_label2">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">No title clip selected. Select to edit or create new.</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <action-widgets>
+          <action-widget response="0">create</action-widget>
+        </action-widgets>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="infobar-action_area1">
+            <property name="can_focus">False</property>
+            <property name="border_width">5</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">6</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="create">
+                <property name="label" translatable="yes">Create text clip</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>
+                <property name="use_action_appearance">False</property>
+                <signal name="clicked" handler="_createCb" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <action-widgets>
+          <action-widget response="0">create</action-widget>
+        </action-widgets>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">0</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkInfoBar" id="infobar2">
+        <property name="visible">True</property>
+        <property name="app_paintable">True</property>
+        <property name="can_focus">False</property>
+        <child internal-child="content_area">
+          <object class="GtkBox" id="infobar-content_area3">
+            <property name="can_focus">False</property>
+            <property name="border_width">8</property>
+            <property name="spacing">16</property>
+            <child>
+              <object class="GtkLabel" id="info_bar_label1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Text clip not in timeline.</property>
+                <property name="wrap">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <action-widgets>
+          <action-widget response="0">insert</action-widget>
+        </action-widgets>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="infobar-action_area3">
+            <property name="can_focus">False</property>
+            <property name="border_width">5</property>
+            <property name="spacing">6</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="insert">
+                <property name="label" translatable="yes">Insert at End of Timeline</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>
+                <property name="use_action_appearance">False</property>
+                <signal name="clicked" handler="_insertEndCb" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <action-widgets>
+          <action-widget response="0">insert</action-widget>
+        </action-widgets>
+      </object>
+      <packing>
+        <property name="expand">False</property>
+        <property name="fill">True</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+    <child>
+      <object class="GtkVBox" id="editing_box">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <child>
+          <object class="GtkToolbar" id="toolbar1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="show_arrow">False</property>
+            <child>
+              <object class="GtkToggleToolButton" id="bold">
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="use_action_appearance">False</property>
+                <property name="label" translatable="yes">Bold</property>
+                <property name="stock_id">gtk-bold</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToggleToolButton" id="italic">
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="use_action_appearance">False</property>
+                <property name="label" translatable="yes">Italic</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-italic</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToggleToolButton" id="underline">
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="use_action_appearance">False</property>
+                <property name="label" translatable="yes">Underline</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-underline</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkSeparatorToolItem" id="toolbutton1">
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="use_action_appearance">False</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToolItem" id="font">
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="use_action_appearance">False</property>
+                <child>
+                  <object class="GtkFontButton" id="fontbutton1">
+                    <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>
+                    <property name="font">Sans 12</property>
+                    <property name="preview_text"/>
+                    <property name="show_preview_entry">False</property>
+                    <signal name="font-set" handler="_fontButtonCb" swapped="no"/>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToolItem" id="font_fore_color">
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="has_tooltip">True</property>
+                <property name="tooltip_markup" translatable="yes">Font color</property>
+                <property name="tooltip_text" translatable="yes">Font color</property>
+                <property name="use_action_appearance">False</property>
+                <child>
+                  <object class="GtkColorButton" id="fore_text_color">
+                    <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>
+                    <property name="title" translatable="yes">Pick a Font colour</property>
+                    <signal name="color-set" handler="_frontTextColorButtonCb" swapped="no"/>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToolItem" id="font_back_color">
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="has_tooltip">True</property>
+                <property name="tooltip_markup" translatable="yes">Font background color</property>
+                <property name="tooltip_text" translatable="yes">Font background color</property>
+                <property name="use_action_appearance">False</property>
+                <child>
+                  <object class="GtkColorButton" id="back_text_color">
+                    <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>
+                    <property name="title" translatable="yes">Pick a Font Foreground Colour</property>
+                    <property name="color">#ffffffffffff</property>
+                    <signal name="color-set" handler="_backTextColorButtonCb" swapped="no"/>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToolItem" id="background_color">
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="has_tooltip">True</property>
+                <property name="tooltip_markup" translatable="yes">Background color</property>
+                <property name="tooltip_text" translatable="yes">Background color</property>
+                <property name="use_action_appearance">False</property>
+                <child>
+                  <object class="GtkColorButton" id="back_color">
+                    <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>
+                    <property name="use_alpha">True</property>
+                    <property name="title" translatable="yes">Pick a Background Colour</property>
+                    <property name="color">#ffffffffffff</property>
+                    <signal name="color-set" handler="_backgroundColorButtonCb" swapped="no"/>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkSeparatorToolItem" id="toolbutton2">
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="use_action_appearance">False</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToggleToolButton" id="markupToggle">
+                <property name="use_action_appearance">False</property>
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="has_tooltip">True</property>
+                <property name="tooltip_markup" translatable="yes">Convert to markup</property>
+                <property name="tooltip_text" translatable="yes">Convert to markup</property>
+                <property name="use_action_appearance">False</property>
+                <property name="label" translatable="yes">&lt;&gt;</property>
+                <property name="use_underline">True</property>
+                <property name="stock_id">gtk-convert</property>
+                <signal name="toggled" handler="_markupToggleCb" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</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="GtkTextView" id="textview1">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="wrap_mode">word</property>
+            <signal name="focus-in-event" handler="_focusedTextView" swapped="no"/>
+            <signal name="focus-out-event" handler="_unfocusedTextView" swapped="no"/>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkExpander" id="expander1">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <child>
+              <object class="GtkVBox" id="box5">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <child>
+                  <object class="GtkAlignment" id="alignment2">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="left_padding">12</property>
+                    <child>
+                      <object class="GtkGrid" id="box4">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="column_spacing">6</property>
+                        <child>
+                          <object class="GtkLabel" id="label6">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="yalign">0.2800000011920929</property>
+                            <property name="label" translatable="yes">Horizontal alignment</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">0</property>
+                            <property name="width">1</property>
+                            <property name="height">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="label2">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="label" translatable="yes">Vertical alignment</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">1</property>
+                            <property name="width">1</property>
+                            <property name="height">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkComboBoxText" id="halignment">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="entry_text_column">0</property>
+                            <property name="id_column">1</property>
+                            <signal name="changed" handler="_updateSource" swapped="no"/>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="top_attach">0</property>
+                            <property name="width">1</property>
+                            <property name="height">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkComboBoxText" id="valignment">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="active">1</property>
+                            <property name="entry_text_column">0</property>
+                            <property name="id_column">1</property>
+                            <signal name="changed" handler="_updateSource" swapped="no"/>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="top_attach">1</property>
+                            <property name="width">1</property>
+                            <property name="height">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSpinButton" id="xpos">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="invisible_char">â</property>
+                            <property name="invisible_char_set">True</property>
+                            <property name="adjustment">position_x_adj</property>
+                            <property name="digits">2</property>
+                            <property name="numeric">True</property>
+                            <signal name="value-changed" handler="_updateSource" swapped="no"/>
+                          </object>
+                          <packing>
+                            <property name="left_attach">2</property>
+                            <property name="top_attach">0</property>
+                            <property name="width">1</property>
+                            <property name="height">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkSpinButton" id="ypos">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="invisible_char">â</property>
+                            <property name="invisible_char_set">True</property>
+                            <property name="adjustment">position_y_adj</property>
+                            <property name="digits">2</property>
+                            <property name="numeric">True</property>
+                            <signal name="value-changed" handler="_updateSource" swapped="no"/>
+                          </object>
+                          <packing>
+                            <property name="left_attach">2</property>
+                            <property name="top_attach">1</property>
+                            <property name="width">1</property>
+                            <property name="height">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object class="GtkLabel" id="label7">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0.38999998569488525</property>
+                <property name="label" translatable="yes">Settings</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">True</property>
+        <property name="fill">True</property>
+        <property name="position">2</property>
+      </packing>
+    </child>
+  </object>
+  <object class="GtkAdjustment" id="position_x_adj">
+    <property name="upper">1</property>
+    <property name="value">0.5</property>
+    <property name="step_increment">0.050000000000000003</property>
+    <property name="page_increment">0.10000000000000001</property>
+  </object>
+  <object class="GtkAdjustment" id="position_y_adj">
+    <property name="upper">1</property>
+    <property name="value">0.5</property>
+    <property name="step_increment">0.050000000000000003</property>
+    <property name="page_increment">0.10000000000000001</property>
+  </object>
+  <object class="GtkAdjustment" id="size_h_adj">
+    <property name="upper">1</property>
+    <property name="value">0.5</property>
+    <property name="step_increment">0.050000000000000003</property>
+    <property name="page_increment">0.10000000000000001</property>
+  </object>
+  <object class="GtkAdjustment" id="size_w_adj">
+    <property name="upper">1</property>
+    <property name="value">0.5</property>
+    <property name="step_increment">0.050000000000000003</property>
+    <property name="page_increment">0.10000000000000001</property>
+  </object>
+</interface>
diff --git a/pitivi/Makefile.am b/pitivi/Makefile.am
index ce15dd4..19a8a26 100644
--- a/pitivi/Makefile.am
+++ b/pitivi/Makefile.am
@@ -26,6 +26,7 @@ pitivi_PYTHON = \
 	tabsmanager.py \
 	transitions.py \
 	viewer.py \
+	titleeditor.py \
 	$(NULL)
 
 BUILT_SOURCES=configure.py
diff --git a/pitivi/mainwindow.py b/pitivi/mainwindow.py
index df3561e..c058995 100644
--- a/pitivi/mainwindow.py
+++ b/pitivi/mainwindow.py
@@ -42,7 +42,7 @@ from pitivi.settings import GlobalSettings
 from pitivi.effects import EffectListWidget
 from pitivi.transitions import TransitionsListWidget
 from pitivi.medialibrary import MediaLibraryWidget, MediaLibraryError
-
+from pitivi.titleeditor import TitleEditor
 from pitivi.utils.misc import show_user_manual
 from pitivi.utils.ui import info_name, beautify_time_delta, SPACING,\
         FILESOURCE_TARGET_ENTRY, URI_TARGET_ENTRY, TYPE_URI_LIST, \
@@ -410,8 +410,11 @@ class PitiviMainWindow(gtk.Window, Loggable):
         self.context_tabs = BaseTabs(instance)
         self.clipconfig = ClipProperties(instance, self.uimanager)
         self.trans_list = TransitionsListWidget(instance, self.uimanager)
+        self.title_editor = TitleEditor(instance, self.uimanager)
         self.context_tabs.append_page(self.clipconfig, gtk.Label(_("Clip configuration")))
         self.context_tabs.append_page(self.trans_list, gtk.Label(_("Transitions")))
+        self.context_tabs.append_page(self.title_editor.widget, gtk.Label(_("Title editor")))
+        self.context_tabs.connect("switch-page", self.title_editor.tab_switched)
         self.clipconfig.show()
         self.trans_list.show()
 
@@ -482,6 +485,8 @@ class PitiviMainWindow(gtk.Window, Loggable):
                 page = 0
             elif tab == "transitions":
                 page = 1
+            elif tab == "title editor":
+                page = 2
             else:
                 self.debug("Invalid context tab switch requested")
                 return False
diff --git a/pitivi/timeline/track.py b/pitivi/timeline/track.py
index 7e5f8a6..c290822 100644
--- a/pitivi/timeline/track.py
+++ b/pitivi/timeline/track.py
@@ -79,6 +79,18 @@ PreferencesDialog.addColorPreference('audioClipBg',
     label=_("Color for audio clips"),
     description=_("The background color for clips in audio tracks."))
 
+GlobalSettings.addConfigOption('titleClipBg',
+    section='user-interface',
+    key='titleclip-background',
+    default=996806336,
+    notify=True)
+
+PreferencesDialog.addColorPreference('titleClipBg',
+    section=_("Appearance"),
+    label=_("Color for title clips"),
+    description=_("The background color for clips in title tracks."))
+
+
 GlobalSettings.addConfigOption('selectedColor',
     section='user-interface',
     key='selected-color',
@@ -496,11 +508,13 @@ class TrackObject(View, goocanvas.Group, Zoomable, Loggable):
         if settings is not None:
             settings.connect("audioClipBgChanged", target)
             settings.connect("videoClipBgChanged", target)
+            settings.connect("titleClipBgChanged", target)
             settings.connect("selectedColorChanged", target)
             settings.connect("clipFontDescChanged", target)
         else:
             self._settings.disconnect_by_func("audioClipBgChanged", target)
             self._settings.disconnect_by_func("videoClipBgChanged", target)
+            self._settings.disconnect_by_func("titleClipBgChanged", target)
             self._settings.disconnect_by_func("selectedColorChanged", target)
             self._settings.disconnect_by_func("clipFontDescChanged", target)
         self._settings = settings
@@ -515,6 +529,7 @@ class TrackObject(View, goocanvas.Group, Zoomable, Loggable):
         if self._settings is not None:
             self._settings.disconnect_by_func("audioClipBgChanged", target)
             self._settings.disconnect_by_func("videoClipBgChanged", target)
+            self._settings.disconnect_by_func("titleClipBgChanged", target)
             self._settings.disconnect_by_func("selectedColorChanged", target)
             self._settings.disconnect_by_func("clipFontDescChanged", target)
         self._settings = None
@@ -579,11 +594,31 @@ class TrackObject(View, goocanvas.Group, Zoomable, Loggable):
             if isinstance(self.element, ges.TrackTransition):
                 if isinstance(self.element, ges.TrackVideoTransition):
                     self.app.gui.trans_list.activate(self.element)
+            elif isinstance(self.element, ges.TrackTitleSource):
+                self.app.gui.switchContextTab("title editor")
+                self.app.gui.title_editor.set_source(self.element.get_timeline_object())
             else:
+                if self.element.get_track().get_property("track_type") == ges.TRACK_TYPE_VIDEO:
+                    has = False
+                    tlobj = self.element.get_timeline_object()
+                    trackobjs = tlobj.get_track_objects()
+                    for trackobj in trackobjs:
+                        if isinstance(trackobj, ges.TrackTextOverlay):
+                            has = True
+                            title = trackobj
+                    if not has:
+                        title = ges.TrackTextOverlay()
+                        title.set_text("")
+                        title.set_start(self.element.get_start())
+                        title.set_duration(self.element.get_duration())
+                        self.element.get_timeline_object().add_track_object(title)
+                        self.element.get_track().add_object(title)
+                    self.app.gui.title_editor.set_source(title)
                 self.app.gui.trans_list.deactivate()
                 self.app.gui.switchContextTab()
             self._selec_indic.props.visibility = goocanvas.ITEM_VISIBLE
         else:
+            self.app.gui.title_editor.set_source(None)
             self._selec_indic.props.visibility = goocanvas.ITEM_INVISIBLE
 
     def _update(self):
@@ -661,6 +696,32 @@ class TrackTransition(TrackObject):
         self.name.props.text = transition.props.transition_type.value_nick
 
 
+class TrackTitleSource(TrackObject):
+    """
+    Subclass of TrackObject for titles
+    """
+    def __init__(self, instance, element, track, timeline, utrack):
+        TrackObject.__init__(self, instance, element, track, timeline, utrack)
+        #self.preview = Preview(self.app, element)
+        for thing in (self.bg, self._selec_indic,
+            self.start_handle, self.end_handle, self.namebg, self.name):
+            self.add_child(thing, -1)
+
+    def _getColor(self):
+        return self.settings.titleClipBg
+
+    def _setElement(self, element):
+        if self.element:
+            text = self.element.get_text()
+            _, _, t, _ = pango.parse_markup(text, -1, u'\x00')
+            #TODO trim text, first line etc
+            self.name.props.text = t
+            twidth, theight = text_size(self.name)
+            self.namewidth = twidth
+            self.nameheight = theight
+            self._update()
+
+
 class TrackFileSource(TrackObject):
     """
     Subclass of TrackObject to allow thumbnailing of objects with URIs
@@ -752,7 +813,11 @@ class Track(goocanvas.Group, Zoomable, Loggable):
     def _objectAddedCb(self, unused_timeline, track_object):
         if isinstance(track_object, ges.TrackTransition):
             self._transitionAdded(track_object)
-        elif isinstance(track_object, ges.TrackSource):
+        elif isinstance(track_object, ges.TrackTitleSource):
+            w = TrackTitleSource(self.app, track_object, self.track, self.timeline, self)
+            self.widgets[track_object] = w
+            self.add_child(w, -1)
+        elif isinstance(track_object, ges.TrackFileSource):
             w = TrackFileSource(self.app, track_object, self.track, self.timeline, self)
             self.widgets[track_object] = w
             self.add_child(w, -1)
diff --git a/pitivi/titleeditor.py b/pitivi/titleeditor.py
new file mode 100644
index 0000000..aba55b0
--- /dev/null
+++ b/pitivi/titleeditor.py
@@ -0,0 +1,760 @@
+# PiTiVi , Non-linear video editor
+#
+#       pitivi/medialibrary.py
+#
+# Copyright (c) 2012, Matas Brazdeikis <matas brazdeikis lt>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this program; if not, write to the
+# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+"""
+Shows title editor
+"""
+import os
+import gtk
+import pango
+import ges
+import gst
+import gobject
+
+from gettext import gettext as _
+from xml.sax import saxutils
+
+from utils.timeline import SELECT
+from pitivi.configure import get_ui_dir, get_pixmap_dir
+from pitivi.utils.loggable import Loggable
+from pitivi.utils.signal import Signallable
+from pitivi.utils.pipeline import Seeker
+INVISIBLE = gtk.gdk.pixbuf_new_from_file(os.path.join(get_pixmap_dir(), "invisible.png"))
+
+
+class PangoBuffer(gtk.TextBuffer):
+    desc_to_attr_table = {
+        'family': [pango.AttrFamily, ""],
+        'size': [pango.AttrSize, 14 * 1024],
+        'style': [pango.AttrStyle, pango.STYLE_NORMAL],
+        'variant': [pango.AttrVariant, pango.VARIANT_NORMAL],
+        'weight': [pango.AttrWeight, pango.WEIGHT_NORMAL],
+        'stretch': [pango.AttrStretch, pango.STRETCH_NORMAL]}
+    # pango ATTR TYPE :(pango attr property / tag property)
+    pango_translation_properties = {
+        pango.ATTR_SIZE: 'size',
+        pango.ATTR_WEIGHT: 'weight',
+        pango.ATTR_UNDERLINE: 'underline',
+        pango.ATTR_STRETCH: 'stretch',
+        pango.ATTR_VARIANT: 'variant',
+        pango.ATTR_STYLE: 'style',
+        pango.ATTR_SCALE: 'scale',
+        pango.ATTR_FAMILY: 'family',
+        pango.ATTR_STRIKETHROUGH: 'strikethrough',
+        pango.ATTR_RISE: 'rise'}
+    pango_type_table = {
+        pango.ATTR_SIZE: pango.AttrInt,
+        pango.ATTR_WEIGHT: pango.AttrInt,
+        pango.ATTR_UNDERLINE: pango.AttrInt,
+        pango.ATTR_STRETCH: pango.AttrInt,
+        pango.ATTR_VARIANT: pango.AttrInt,
+        pango.ATTR_STYLE: pango.AttrInt,
+        pango.ATTR_SCALE: pango.AttrFloat,
+        pango.ATTR_FAMILY: pango.AttrString,
+        pango.ATTR_FONT_DESC: pango.AttrFontDesc,
+        pango.ATTR_STRIKETHROUGH: pango.AttrInt,
+        pango.ATTR_BACKGROUND: pango.AttrColor,
+        pango.ATTR_FOREGROUND: pango.AttrColor,
+        pango.ATTR_RISE: pango.AttrInt}
+
+    attval_to_markup = {
+        'underline': {pango.UNDERLINE_SINGLE: 'single',
+                      pango.UNDERLINE_DOUBLE: 'double',
+                      pango.UNDERLINE_LOW: 'low',
+                      pango.UNDERLINE_NONE: 'none'
+                      },
+        'stretch': {pango.STRETCH_ULTRA_EXPANDED: 'ultraexpanded',
+                    pango.STRETCH_EXPANDED: 'expanded',
+                    pango.STRETCH_EXTRA_EXPANDED: 'extraexpanded',
+                    pango.STRETCH_EXTRA_CONDENSED: 'extracondensed',
+                    pango.STRETCH_ULTRA_CONDENSED: 'ultracondensed',
+                    pango.STRETCH_CONDENSED: 'condensed',
+                    pango.STRETCH_NORMAL: 'normal',
+                    },
+        'variant': {pango.VARIANT_NORMAL: 'normal',
+                    pango.VARIANT_SMALL_CAPS: 'smallcaps',
+                    },
+        'style': {pango.STYLE_NORMAL: 'normal',
+                  pango.STYLE_OBLIQUE: 'oblique',
+                  pango.STYLE_ITALIC: 'italic',
+                  },
+        'stikethrough': {1: 'true',
+                         True: 'true',
+                         0: 'false',
+                         False: 'false'
+                         }}
+
+    def __init__(self):
+        self.tagdict = {}
+        self.tags = {}
+        gtk.TextBuffer.__init__(self)
+
+    def set_text(self, txt):
+        gtk.TextBuffer.set_text(self, "")
+        suc, self.parsed, self.txt, self.separator = pango.parse_markup(txt, -1, u'\x00')
+        if not suc:
+            oldtxt = txt
+            txt = saxutils.escape(txt)
+            self.warn("Marked text is not correct. Escape %s to %s", oldtxt, txt)
+            suc, self.parsed, self.txt, self.separator = pango.parse_markup(txt, -1, u'\x00')
+        self.attrIter = self.parsed.get_iterator()
+        self.add_iter_to_buffer()
+        while self.attrIter.next():
+            self.add_iter_to_buffer()
+
+    def add_iter_to_buffer(self):
+        it_range = self.attrIter.range()
+        font, lang, attrs = self.attrIter.get_font()
+        tags = self.get_tags_from_attrs(font, lang, attrs)
+        text = self.txt[it_range[0]:it_range[1]]
+        if tags:
+            self.insert_with_tags(self.get_end_iter(), text, *tags)
+        else:
+            self.insert_with_tags(self.get_end_iter(), text, *tags)
+
+    def get_tags_from_attrs(self, font, lang, attrs):
+        tags = []
+        if font:
+            fontattrs = self.fontdesc_to_attrs(font)
+            fontdesc = font.to_string()
+            if fontattrs:
+                attrs.extend(fontattrs)
+        if lang:
+            if not lang in self.tags:
+                tag = self.create_tag()
+                tag.set_property('language', lang)
+                self.tags[lang] = tag
+            tags.append(self.tags[lang])
+        if attrs:
+            for a in attrs:
+                #FIXME remove on pango fix
+                type_ = a.klass.type
+                klass = a.klass
+                start_index = a.start_index
+                end_index = a.end_index
+                a.__class__ = self.pango_type_table[type_]
+                a.type = type_
+                a.start_index = start_index
+                a.end_index = end_index
+                a.klass = klass
+                if a.type == pango.ATTR_FOREGROUND:
+                    gdkcolor = self.pango_color_to_gdk(a.color)
+                    key = 'foreground%s' % self.color_to_hex(gdkcolor)
+                    if not key in self.tags:
+                        self.tags[key] = self.create_tag()
+                        self.tags[key].set_property('foreground-gdk', gdkcolor)
+                        self.tagdict[self.tags[key]] = {}
+                        self.tagdict[self.tags[key]]['foreground'] = "#%s" % self.color_to_hex(gdkcolor)
+                    tags.append(self.tags[key])
+                if a.type == pango.ATTR_BACKGROUND:
+                    gdkcolor = self.pango_color_to_gdk(a.color)
+                    key = 'background%s' % self.color_to_hex(gdkcolor)
+                    if not key in self.tags:
+                        self.tags[key] = self.create_tag()
+                        self.tags[key].set_property('background-gdk', gdkcolor)
+                        self.tagdict[self.tags[key]] = {}
+                        self.tagdict[self.tags[key]]['background'] = "#%s" % self.color_to_hex(gdkcolor)
+                    tags.append(self.tags[key])
+                if a.type in self.pango_translation_properties:
+                    prop = self.pango_translation_properties[a.type]
+                    val = getattr(a, 'value')
+                    #tag.set_property(prop, val)
+                    mval = val
+                    if prop in self.attval_to_markup:
+                        if val in self.attval_to_markup[prop]:
+                            mval = self.attval_to_markup[prop][val]
+                    key = "%s%s" % (prop, val)
+                    if not key in self.tags:
+                        self.tags[key] = self.create_tag()
+                        self.tags[key].set_property(prop, val)
+                        self.tagdict[self.tags[key]] = {}
+                        self.tagdict[self.tags[key]][prop] = mval
+                    tags.append(self.tags[key])
+        return tags
+
+    def get_tags(self):
+        tagdict = {}
+        for pos in range(self.get_char_count()):
+            iter = self.get_iter_at_offset(pos)
+            for tag in iter.get_tags():
+                if tag in tagdict:
+                    if tagdict[tag][-1][1] == pos - 1:
+                        tagdict[tag][-1] = (tagdict[tag][-1][0], pos)
+                    else:
+                        tagdict[tag].append((pos, pos))
+                else:
+                    tagdict[tag] = [(pos, pos)]
+        return tagdict
+
+    def split(self, interval, split_interval):
+        #We want as less intervals as posible
+        # interval represented []
+        # split interval represented {}
+        if interval == split_interval:
+            return [interval]
+        if interval[1] < split_interval[0] or split_interval[1] < interval[0]:
+            return [interval]
+
+        if interval[0] == split_interval[0]:
+            if interval[1] < split_interval[1]:
+                return [interval]
+            else:
+                return [(interval[0], split_interval[1]),
+                        (split_interval[1] + 1, interval[1])]
+
+        if interval[0] < split_interval[0]:
+            if interval[1] == split_interval[1]:
+                return [(interval[0], split_interval[0] - 1),
+                         (split_interval[0], interval[1])]
+            elif interval[1] < split_interval[1]:
+                return [(interval[0], split_interval[0] - 1),
+                         (split_interval[0], interval[1])]
+            else:  # interval[1] > split_interval[1]
+                return [(interval[0], split_interval[0] - 1),
+                         (split_interval[0], split_interval[1]),
+                         (split_interval[1] + 1, interval[1])]
+
+        if interval[0] > split_interval[0]:
+            if interval[1] == split_interval[1]:
+                return [interval]
+            elif interval[1] < split_interval[1]:
+                return [interval]
+            else:  # interval[1] > split_interval[1]
+                return [(interval[0], split_interval[1]),
+                        (split_interval[1] + 1, interval[1])]
+
+    def split_overlap(self, tagdict):
+        intervals = []
+        for k, v in tagdict.items():
+            #Split by exiting intervals
+            tmpint = v
+            for i in intervals:
+                iterint = tmpint
+                tmpint = []
+                for st, e in iterint:
+                    tmpint.extend(self.split((st, e), i))
+            tagdict[k] = tmpint
+            #Add new intervals
+            intervals.extend(tmpint)
+        return tagdict
+
+    def get_text(self, start=None, end=None, include_hidden_chars=True):
+        tagdict = self.get_tags()
+        if not start:
+            start = self.get_start_iter()
+        if not end:
+            end = self.get_end_iter()
+        txt = unicode(gtk.TextBuffer.get_text(self, start, end, True))
+        #Important step, split that no tags overlap
+        tagdict = self.split_overlap(tagdict)
+        cuts = {}
+        for k, v in tagdict.items():
+            stag, etag = self.tag_to_markup(k)
+            for st, e in v:
+                if st in cuts:
+                    #add start tags second
+                    cuts[st].append(stag)
+                else:
+                    cuts[st] = [stag]
+                if e + 1 in cuts:
+                    #add end tags first
+                    cuts[e + 1] = [etag] + cuts[e + 1]
+                else:
+                    cuts[e + 1] = [etag]
+        last_pos = 0
+        outbuff = ""
+        cut_indices = cuts.keys()
+        cut_indices.sort()
+        soffset = start.get_offset()
+        eoffset = end.get_offset()
+        cut_indices = filter(lambda i: eoffset >= i >= soffset, cut_indices)
+        for c in cut_indices:
+            if not last_pos == c:
+                outbuff += saxutils.escape(txt[last_pos:c])
+                last_pos = c
+            for tag in cuts[c]:
+                outbuff += tag
+        outbuff += saxutils.escape(txt[last_pos:])
+        return outbuff
+
+    def tag_to_markup(self, tag):
+        stag = "<span"
+        for k, v in self.tagdict[tag].items():
+            #family in gtk, face in pango mark language
+            if k == "family":
+                k = "face"
+            stag += ' %s="%s"' % (k, v)
+        stag += ">"
+        return stag, "</span>"
+
+    def fontdesc_to_attrs(self, font):
+        nicks = font.get_set_fields().value_nicks
+        attrs = []
+        for n in nicks:
+            if n in self.desc_to_attr_table:
+                Attr, norm = self.desc_to_attr_table[n]
+                # create an attribute with our current value
+                attrs.append(Attr(getattr(font, 'get_%s' % n)()))
+        return attrs
+
+    def pango_color_to_gdk(self, pc):
+        return gtk.gdk.Color(pc.red, pc.green, pc.blue)
+
+    def color_to_hex(self, color):
+        hexstring = ""
+        for col in 'red', 'green', 'blue':
+            hexfrag = hex(getattr(color, col) / (16 * 16)).split("x")[1]
+            if len(hexfrag) < 2:
+                hexfrag = "0" + hexfrag
+            hexstring += hexfrag
+        return hexstring
+
+    def apply_font_and_attrs(self, font, attrs):
+        tags = self.get_tags_from_attrs(font, None, attrs)
+        for t in tags:
+            self.apply_tag_to_selection(t)
+
+    def remove_font_and_attrs(self, font, attrs):
+        tags = self.get_tags_from_attrs(font, None, attrs)
+        for t in tags:
+            self.remove_tag_from_selection(t)
+
+    def get_selection(self):
+        bounds = self.get_selection_bounds()
+        if not bounds:
+            iter = self.get_iter_at_mark(self.insert_mark)
+            if iter.inside_word():
+                start_pos = iter.get_offset()
+                iter.forward_word_end()
+                word_end = iter.get_offset()
+                iter.backward_word_start()
+                word_start = iter.get_offset()
+                iter.set_offset(start_pos)
+                bounds = (self.get_iter_at_offset(word_start),
+                          self.get_iter_at_offset(word_end + 1))
+            else:
+                bounds = (iter, self.get_iter_at_offset(iter.get_offset() + 1))
+        return bounds
+
+    def apply_tag_to_selection(self, tag):
+        selection = self.get_selection()
+        if selection:
+            self.apply_tag(tag, *selection)
+        self.emit("changed")
+
+    def remove_tag_from_selection(self, tag):
+        selection = self.get_selection()
+        if selection:
+            self.remove_tag(tag, *selection)
+        self.emit("changed")
+
+    def remove_all_tags(self):
+        selection = self.get_selection()
+        if selection:
+            for t in self.tags.values():
+                self.remove_tag(t, *selection)
+
+
+class InteractivePangoBuffer(PangoBuffer):
+    def __init__(self, normal_button=None, toggle_widget_alist=[]):
+        """
+        An interactive interface to allow marking up a gtk.TextBuffer.
+        txt is initial text, with markup.  buf is the gtk.TextBuffer
+        normal_button is a widget whose clicked signal will make us normal
+        toggle_widget_alist is a list that looks like this:
+            [(widget,(font, attr)), (widget2, (font, attr))]
+         """
+        PangoBuffer.__init__(self)
+        if normal_button:
+            normal_button.connect('clicked', lambda *args: self.remove_all_tags())
+        self.tag_widgets = {}
+        self.internal_toggle = False
+        self.insert_mark = self.get_insert()
+        self.connect('mark-set', self._mark_set_cb)
+        self.connect('changed', self._changed_cb)
+        for w, tup in toggle_widget_alist:
+            self.setup_widget(w, *tup)
+
+    def set_text(self, txt):
+        self.disconnect_by_func(self._changed_cb)
+        self.disconnect_by_func(self._mark_set_cb)
+        PangoBuffer.set_text(self, txt)
+        self.connect('changed', self._changed_cb)
+        self.connect('mark-set', self._mark_set_cb)
+
+    def setup_widget_from_pango(self, widg, markupstring):
+        """setup widget from a pango markup string"""
+        #font = pango.FontDescription(fontstring)
+        suc, a, t, s = pango.parse_markup(markupstring, -1, u'\x00')
+        ai = a.get_iterator()
+        font, lang, attrs = ai.get_font()
+
+        return self.setup_widget(widg, font, attrs)
+
+    def setup_widget(self, widg, font, attr):
+        tags = self.get_tags_from_attrs(font, None, attr)
+        self.tag_widgets[tuple(tags)] = widg
+        return widg.connect('toggled', self._toggle, tags)
+
+    def _toggle(self, widget, tags):
+        if self.internal_toggle:
+            return
+        if widget.get_active():
+            for t in tags:
+                self.apply_tag_to_selection(t)
+        else:
+            for t in tags:
+                self.remove_tag_from_selection(t)
+
+    def _mark_set_cb(self, buffer, iter, mark, *params):
+        # Every time the cursor moves, update our widgets that reflect
+        # the state of the text.
+        if hasattr(self, '_in_mark_set') and self._in_mark_set:
+            return
+        self._in_mark_set = True
+        if mark.get_name() == 'insert':
+            for tags, widg in self.tag_widgets.items():
+                active = True
+                for t in tags:
+                    if not iter.has_tag(t):
+                        active = False
+                self.internal_toggle = True
+                widg.set_active(active)
+                self.internal_toggle = False
+        if hasattr(self, 'last_mark'):
+            self.move_mark(self.last_mark, iter)
+        else:
+            self.last_mark = self.create_mark('last', iter, left_gravity=True)
+        self._in_mark_set = False
+
+    def _changed_cb(self, tb):
+        if not hasattr(self, 'last_mark'):
+            return
+        # If our insertion point has a mark, we want to apply the tag
+        # each time the user types...
+        old_itr = self.get_iter_at_mark(self.last_mark)
+        insert_itr = self.get_iter_at_mark(self.insert_mark)
+        if old_itr != insert_itr:
+            # Use the state of our widgets to determine what
+            # properties to apply...
+            for tags, w in self.tag_widgets.items():
+                if w.get_active():
+                    for t in tags:
+                        self.apply_tag(t, old_itr, insert_itr)
+
+
+class TitleEditor(Loggable):
+    def __init__(self, instance, uimap):
+        Loggable.__init__(self)
+        Signallable.__init__(self)
+        self.app = instance
+        self.bt = {}
+        self.settings = {}
+        self.source = None
+        self.created = False
+        self.seeker = Seeker()
+
+        #Drag attributes
+        self._drag_events = []
+        self._drag_connected = False
+        self._tab_opened = False
+
+        #Creat UI
+        self._createUI()
+        self.textbuffer = gtk.TextBuffer()
+        self.pangobuffer = InteractivePangoBuffer()
+        self.textarea.set_buffer(self.pangobuffer)
+
+        #Conect updates
+        self.textbuffer.connect("changed", self._updateSourceText)
+        self.pangobuffer.connect("changed", self._updateSourceText)
+
+        #Connect buttons
+        self.pangobuffer.setup_widget_from_pango(self.bt["bold"], "<b>bold</b>")
+        self.pangobuffer.setup_widget_from_pango(self.bt["italic"], "<i>italic</i>")
+        self.pangobuffer.setup_widget_from_pango(self.bt["underline"], "<u>underline</u>")
+
+    def _createUI(self):
+        builder = gtk.Builder()
+        builder.add_from_file(os.path.join(get_ui_dir(), "titleeditor.ui"))
+        builder.connect_signals(self)
+        self.widget = builder.get_object("box1")
+        self.editing_box = builder.get_object("editing_box")
+        self.textarea = builder.get_object("textview1")
+        self.markup_button = builder.get_object("markupToggle")
+        self.info_bar_create = builder.get_object("infobar1")
+        self.info_bar_insert = builder.get_object("infobar2")
+        buttons = ["bold", "italic", "underline", "font", "font_fore_color", "font_back_color", "back_color"]
+
+        for button in buttons:
+            self.bt[button] = builder.get_object(button)
+        settings = ["valignment", "halignment", "xpos", "ypos"]
+
+        for setting in settings:
+            self.settings[setting] = builder.get_object(setting)
+
+        for n, en in {_("Custom"): "position",
+                      _("Top"): "top",
+                      _("Center"): "center",
+                      _("Bottom"): "bottom",
+                      _("Baseline"): "baseline"}.items():
+            self.settings["valignment"].append(en, n)
+
+        for n, en in {_("Custom"): "position",
+                      _("Left"): "left",
+                      _("Center"): "center",
+                      _("Right"): "right"}.items():
+            self.settings["halignment"].append(en, n)
+        self.set_sensitive(False)
+
+    def _focusedTextView(self, widget, notused_event):
+        self.app.gui.timeline_ui.playhead_actions.set_sensitive(False)
+        self.app.gui.timeline_ui.selection_actions.set_sensitive(False)
+
+    def _unfocusedTextView(self, widget, notused_event):
+        self.app.gui.timeline_ui.playhead_actions.set_sensitive(True)
+        self.app.gui.timeline_ui.selection_actions.set_sensitive(True)
+
+    def _backgroundColorButtonCb(self, widget):
+        self.textarea.modify_base(self.textarea.get_state(), widget.get_color())
+        color = widget.get_rgba()
+        color_int = 0
+        color_int += int(color.red * 255) * 256 ** 2
+        color_int += int(color.green * 255) * 256 ** 1
+        color_int += int(color.blue * 255) * 256 ** 0
+        color_int += int(color.alpha * 255) * 256 ** 3
+        self.debug("Setting title background color to %s", hex(color_int))
+        self.source.set_background(color_int)
+
+    def _frontTextColorButtonCb(self, widget):
+        suc, a, t, s = pango.parse_markup("<span color='" + widget.get_color().to_string() + "'>color</span>", -1, u'\x00')
+        ai = a.get_iterator()
+        font, lang, attrs = ai.get_font()
+        tags = self.pangobuffer.get_tags_from_attrs(None, None, attrs)
+        self.pangobuffer.apply_tag_to_selection(tags[0])
+
+    def _backTextColorButtonCb(self, widget):
+        suc, a, t, s = pango.parse_markup("<span background='" + widget.get_color().to_string() + "'>color</span>", -1, u'\x00')
+        ai = a.get_iterator()
+        font, lang, attrs = ai.get_font()
+        tags = self.pangobuffer.get_tags_from_attrs(None, None, attrs)
+        self.pangobuffer.apply_tag_to_selection(tags[0])
+
+    def _fontButtonCb(self, widget):
+        font_desc = widget.get_font_name().split(" ")
+        font_face = " ".join(font_desc[:-1])
+        font_size = str(int(font_desc[-1]) * 1024)
+        text = "<span face='" + font_face + "'><span size='" + font_size + "'>text</span></span>"
+        suc, a, t, s = pango.parse_markup(text, -1, u'\x00')
+        ai = a.get_iterator()
+        font, lang, attrs = ai.get_font()
+        tags = self.pangobuffer.get_tags_from_attrs(font, None, attrs)
+        for tag in tags:
+            self.pangobuffer.apply_tag_to_selection(tag)
+
+    def _markupToggleCb(self, markup_button):
+        self.textbuffer.disconnect_by_func(self._updateSourceText)
+        self.pangobuffer.disconnect_by_func(self._updateSourceText)
+        if markup_button.get_active():
+            for name in self.bt:
+                self.bt[name].set_sensitive(False)
+            self.textbuffer.set_text(self.pangobuffer.get_text())
+            self.textarea.set_buffer(self.textbuffer)
+        else:
+            for name in self.bt:
+                self.bt[name].set_sensitive(True)
+            self.pangobuffer.set_text(
+                self.textbuffer.get_text(self.textbuffer.get_start_iter(),
+                                         self.textbuffer.get_end_iter(), True))
+            self.textarea.set_buffer(self.pangobuffer)
+        self.textbuffer.connect("changed", self._updateSourceText)
+        self.pangobuffer.connect("changed", self._updateSourceText)
+
+    def set_sensitive(self, sensitive):
+        if sensitive:
+            self.info_bar_create.hide()
+            self.editing_box.set_sensitive(True)
+        else:
+            self.info_bar_create.show()
+            self.info_bar_insert.hide()
+            self.editing_box.set_sensitive(False)
+
+        self.preview(sensitive)
+
+    def _updateFromSource(self):
+        if self.source is not None:
+            self.log("Title text set to %s", self.source.get_text())
+
+            self.pangobuffer.set_text(self.source.get_text())
+            self.textbuffer.set_text(self.source.get_text())
+            self.settings['xpos'].set_value(self.source.get_xpos())
+            self.settings['ypos'].set_value(self.source.get_ypos())
+            self.settings['valignment'].set_active_id(self.source.get_valignment().value_name)
+            self.settings['halignment'].set_active_id(self.source.get_halignment().value_name)
+            if hasattr(self.source, "get_background"):
+                self.bt["back_color"].set_visible(True)
+                color = self.source.get_background()
+                color = gtk.gdk.RGBA(color / 256 ** 2 % 256 / 255.,
+                                     color / 256 ** 1 % 256 / 255.,
+                                     color / 256 ** 0 % 256 / 255.,
+                                     color / 256 ** 3 % 256 / 255.)
+                self.bt["back_color"].set_rgba(color)
+            else:
+                self.bt["back_color"].set_visible(False)
+
+    def _updateSourceText(self, updated_obj):
+        if self.source is not None:
+            if self.markup_button.get_active():
+                text = self.textbuffer.get_text(self.textbuffer.get_start_iter(),
+                                                self.textbuffer.get_end_iter(),
+                                                True)
+            else:
+                text = self.pangobuffer.get_text()
+            self.log("Source text updated to %s", text)
+            self.source.set_text(text)
+            self.preview()
+
+    def _updateSource(self, updated_obj):
+        if self.source is not None:
+            for name, obj in self.settings.items():
+                if obj == updated_obj:
+                    if name == "valignment":
+                        self.source.set_valignment(getattr(ges.TextVAlign, obj.get_active_id().upper()))
+                        self.settings["ypos"].set_visible(obj.get_active_id() == "position")
+                    elif name == "halignment":
+                        self.source.set_halignment(getattr(ges.TextHAlign, obj.get_active_id().upper()))
+                        self.settings["xpos"].set_visible(obj.get_active_id() == "position")
+                    elif name == "xpos":
+                        self.settings["halignment"].set_active_id("position")
+                        self.source.set_xpos(obj.get_value())
+                    elif name == "ypos":
+                        self.settings["valignment"].set_active_id("position")
+                        self.source.set_ypos(obj.get_value())
+                    self.preview()
+                    return
+
+    def _reset(self):
+        #TODO: reset not only text
+        self.markup_button.set_active(False)
+        self.pangobuffer.set_text("")
+        self.textbuffer.set_text("")
+        #Set right buffer
+        self._markupToggleCb(self.markup_button)
+
+    def set_source(self, source, created=False):
+        self.debug("Source set to %s", str(source))
+        self.source = None
+        self._reset()
+        self.created = created
+        if source is None:
+            self.set_sensitive(False)
+        else:
+            self.source = source
+            self._updateFromSource()
+            self.set_sensitive(True)
+
+    def _createCb(self, unused_button):
+        source = ges.TimelineTitleSource()
+        source.set_text("")
+        source.set_duration(long(gst.SECOND * 5))
+        #Show insert infobar only if created new source
+        self.info_bar_insert.show()
+        self.set_source(source, True)
+
+    def _insertEndCb(self, unused_button):
+        self.info_bar_insert.hide()
+        self.app.gui.timeline_ui.insertEnd([self.source])
+        self.app.gui.timeline_ui.timeline.selection.setToObj(self.source, SELECT)
+        #After insertion consider as not created
+        self.created = False
+
+    def preview(self, show=True):
+        if not show:
+            #Disconect
+            if self._drag_connected:
+                self.app.gui.viewer.target.disconnect_by_func(self.drag_notify_event)
+                self.app.gui.viewer.target.disconnect_by_func(self.drag_press_event)
+                self.app.gui.viewer.target.disconnect_by_func(self.drag_release_event)
+                self._drag_connected = False
+        elif self.source is not None and not self.created:
+            self.seeker.flush()
+            if not self._drag_connected and self._tab_opened:
+                #If source is in timeline and title tab opened enable title drag
+                self._drag_connected = True
+                self.app.gui.viewer.target.connect("motion-notify-event", self.drag_notify_event)
+                self.app.gui.viewer.target.connect("button-press-event", self.drag_press_event)
+                self.app.gui.viewer.target.connect("button-release-event", self.drag_release_event)
+
+    def drag_press_event(self, widget, event):
+        if event.button == 1:
+            self._drag_events = [(event.x, event.y)]
+            #Update drag by drag event change, but not too often
+            self.timeout = gobject.timeout_add(100, self.drag_update_event)
+            #If drag goes out for 0.3 second, and do not come back, consider drag end
+            self._drag_updated = True
+            self.timeout = gobject.timeout_add(1000, self.drag_posible_end_event)
+
+    def drag_posible_end_event(self):
+        if self._drag_updated:
+            #Updated during last timeout, wait more
+            self._drag_updated = False
+            return True
+        else:
+            #Not updated - posibly out of bounds, stop drag
+            self.log("Drag timeout")
+            self._drag_events = []
+            return False
+
+    def drag_update_event(self):
+        if len(self._drag_events) > 0:
+            st = self._drag_events[0]
+            self._drag_events = [self._drag_events[-1]]
+            e = self._drag_events[0]
+            xdiff = e[0] - st[0]
+            ydiff = e[1] - st[1]
+            xdiff /= self.app.gui.viewer.target.get_allocated_width()
+            ydiff /= self.app.gui.viewer.target.get_allocated_height()
+            newxpos = self.settings["xpos"].get_value() + xdiff
+            newypos = self.settings["ypos"].get_value() + ydiff
+            self.settings["xpos"].set_value(newxpos)
+            self.settings["ypos"].set_value(newypos)
+            self.seeker.flush()
+            return True
+        else:
+            return False
+
+    def drag_notify_event(self, widget, event):
+        if len(self._drag_events) > 0 and event.get_state() & gtk.gdk.BUTTON1_MASK:
+            self._drag_updated = True
+            self._drag_events.append((event.x, event.y))
+            st = self._drag_events[0]
+            e = self._drag_events[-1]
+
+    def drag_release_event(self, widget, event):
+        self._drag_events = []
+
+    def tab_switched(self, unused_notebook, arg1, arg2):
+        if arg2 == 2:
+            self._tab_opened = True
+            self.preview(True)
+        else:
+            self._tab_opened = False
+            self.preview(False)
diff --git a/pitivi/utils/timeline.py b/pitivi/utils/timeline.py
index ecdc085..eb6383e 100644
--- a/pitivi/utils/timeline.py
+++ b/pitivi/utils/timeline.py
@@ -126,12 +126,12 @@ class Selection(Signallable):
 
         for obj in old_selection - self.selected:
             for tckobj in obj.get_track_objects():
-                if not isinstance(tckobj, ges.TrackEffect):
+                if not isinstance(tckobj, ges.TrackEffect) and not isinstance(tckobj, ges.TrackTextOverlay):
                     tckobj.selected.selected = False
 
         for obj in self.selected - old_selection:
             for tckobj in obj.get_track_objects():
-                if not isinstance(tckobj, ges.TrackEffect):
+                if not isinstance(tckobj, ges.TrackEffect) and not isinstance(tckobj, ges.TrackTextOverlay):
                     tckobj.selected.selected = True
 
 
diff --git a/pitivi/viewer.py b/pitivi/viewer.py
index 970a2ef..33ef513 100644
--- a/pitivi/viewer.py
+++ b/pitivi/viewer.py
@@ -24,6 +24,7 @@ import gobject
 import gtk
 import gst
 import cairo
+import ges
 
 from gettext import gettext as _
 from time import time
@@ -391,7 +392,7 @@ class PitiviViewer(gtk.VBox, Loggable):
         """
         While a clip is being trimmed, show a live preview of it.
         """
-        if tl_obj.props.is_image or not hasattr(tl_obj, "get_uri"):
+        if isinstance(tl_obj, ges.TimelineTitleSource) or tl_obj.props.is_image or not hasattr(tl_obj, "get_uri"):
             self.log("%s is an image or has no URI, so not previewing trim" % tl_obj)
             return False
 



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