Commits

Akira TAGOH committed 151c544

Add font substitution support

Comments (0)

Files changed (10)

 	fonts-tweak-alias.ui	\
 	fonts-tweak-lang.ui	\
 	fonts-tweak-prop.ui	\
+	fonts-tweak-subst.ui	\
 	$(NULL)
 
 ##
     <property name="can_focus">False</property>
     <property name="border_width">5</property>
     <property name="type_hint">dialog</property>
+    <signal name="close" handler="on_chooser_dialog_close" swapped="no"/>
     <signal name="show" handler="on_chooser_dialog_show" swapped="no"/>
     <signal name="response" handler="on_chooser_dialog_response" swapped="no"/>
     <child internal-child="vbox">
             <child>
               <object class="GtkButton" id="button-cancel">
                 <property name="label">gtk-cancel</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>
                 <property name="use_stock">True</property>
               </object>
               <packing>
             <child>
               <object class="GtkButton" id="button-add">
                 <property name="label">gtk-add</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>
                 <property name="use_stock">True</property>
               </object>
               <packing>

data/fonts-tweak-prop.ui

 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
   <!-- interface-requires gtk+ 3.0 -->
-  <object class="GtkListStore" id="fonts-list">
-    <columns>
-      <!-- column-name name -->
-      <column type="gchararray"/>
-    </columns>
-  </object>
   <object class="GtkListStore" id="hintstyle-list">
     <columns>
       <!-- column-name name -->
       </row>
     </data>
   </object>
+  <object class="GtkListStore" id="fonts-list">
+    <columns>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
   <object class="GtkBox" id="fonts-prop-ui">
     <property name="visible">True</property>
     <property name="can_focus">False</property>

data/fonts-tweak-subst.ui

+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <object class="GtkDialog" id="add-subst-dialog">
+    <property name="can_focus">False</property>
+    <property name="border_width">5</property>
+    <property name="type_hint">dialog</property>
+    <signal name="close" handler="on_add_subst_dialog_close" swapped="no"/>
+    <signal name="show" handler="on_add_subst_dialog_show" swapped="no"/>
+    <child internal-child="vbox">
+      <object class="GtkBox" id="dialog-vbox1">
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <property name="spacing">2</property>
+        <child internal-child="action_area">
+          <object class="GtkButtonBox" id="dialog-action_area1">
+            <property name="can_focus">False</property>
+            <child>
+              <object class="GtkButton" id="button-cancel">
+                <property name="label">gtk-cancel</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="button-add">
+                <property name="label">gtk-add</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">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="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox" id="box3">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="border_width">5</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">6</property>
+            <child>
+              <object class="GtkLabel" id="label4">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Select a font to add</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkComboBox" id="combobox">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="model">liststore</property>
+                <property name="has_entry">True</property>
+                <property name="entry_text_column">0</property>
+                <child internal-child="entry">
+                  <object class="GtkEntry" id="combobox-entry">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="has_focus">True</property>
+                    <property name="is_focus">True</property>
+                    <property name="placeholder_text">Font name to be substituted</property>
+                    <property name="completion">entrycompletion</property>
+                    <property name="input_hints">GTK_INPUT_HINT_NO_SPELLCHECK | GTK_INPUT_HINT_WORD_COMPLETION | GTK_INPUT_HINT_NONE</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="0">button-cancel</action-widget>
+      <action-widget response="0">button-add</action-widget>
+    </action-widgets>
+  </object>
+  <object class="GtkListStore" id="assigned-fonts">
+    <columns>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkEntryCompletion" id="entrycompletion">
+    <property name="model">liststore</property>
+    <property name="text_column">0</property>
+    <property name="inline_completion">True</property>
+    <property name="popup_single_match">False</property>
+    <property name="inline_selection">True</property>
+    <child>
+      <object class="GtkCellRendererText" id="cellrenderertext1"/>
+      <attributes>
+        <attribute name="text">0</attribute>
+      </attributes>
+    </child>
+  </object>
+  <object class="GtkBox" id="fonts-subst-ui">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="margin_left">12</property>
+    <property name="margin_right">12</property>
+    <property name="margin_top">12</property>
+    <property name="margin_bottom">12</property>
+    <property name="spacing">12</property>
+    <child>
+      <object class="GtkBox" id="box1">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="orientation">vertical</property>
+        <child>
+          <object class="GtkScrolledWindow" id="scrolledwindow1">
+            <property name="visible">True</property>
+            <property name="can_focus">True</property>
+            <property name="shadow_type">in</property>
+            <property name="min_content_width">150</property>
+            <child>
+              <object class="GtkTreeView" id="treeview-subst-fonts-list">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="model">subst-fonts</property>
+                <property name="headers_visible">False</property>
+                <property name="headers_clickable">False</property>
+                <property name="search_column">0</property>
+                <child internal-child="selection">
+                  <object class="GtkTreeSelection" id="treeview-selection-subst">
+                    <signal name="changed" handler="on_treeview_selection_subst_changed" swapped="no"/>
+                  </object>
+                </child>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkToolbar" id="toolbar-subst">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="toolbar_style">icons</property>
+            <property name="show_arrow">False</property>
+            <property name="icon_size">1</property>
+            <style>
+              <class name="inline-toolbar"/>
+            </style>
+            <child>
+              <object class="GtkToolButton" id="add-subst">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="tooltip_text" translatable="yes">Add a substitute font</property>
+                <property name="label" translatable="yes">Add a substitute font</property>
+                <property name="use_underline">True</property>
+                <property name="icon_name">list-add-symbolic</property>
+                <signal name="clicked" handler="on_add_subst_clicked" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="homogeneous">True</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToolButton" id="remove-subst">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="tooltip_text" translatable="yes">Remove the substitute font</property>
+                <property name="label" translatable="yes">Remove the substitute font</property>
+                <property name="use_underline">True</property>
+                <property name="icon_name">list-remove-symbolic</property>
+                <signal name="clicked" handler="on_remove_subst_clicked" 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">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="GtkNotebook" id="notebook-subst-pages">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <property name="show_tabs">False</property>
+        <property name="show_border">False</property>
+        <child>
+          <object class="GtkBox" id="box2">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkScrolledWindow" id="scrolledwindow2">
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="shadow_type">in</property>
+                <child>
+                  <object class="GtkTreeView" id="treeview-assigned-fonts-list">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="model">assigned-fonts</property>
+                    <property name="headers_visible">False</property>
+                    <property name="headers_clickable">False</property>
+                    <property name="search_column">0</property>
+                    <child internal-child="selection">
+                      <object class="GtkTreeSelection" id="treeview-selection-assigned">
+                        <signal name="changed" handler="on_treeview_selection_assigned_changed" swapped="no"/>
+                      </object>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkToolbar" id="toolbar-subst-assigned">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="toolbar_style">icons</property>
+                <property name="show_arrow">False</property>
+                <property name="icon_size">1</property>
+                <style>
+                  <class name="inline-toolbar"/>
+                </style>
+                <child>
+                  <object class="GtkToolButton" id="add-font-assigned">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="tooltip_text" translatable="yes">Add a font to the substitute font</property>
+                    <property name="label" translatable="yes">Add a font to the substitute font</property>
+                    <property name="use_underline">True</property>
+                    <property name="icon_name">list-add-symbolic</property>
+                    <signal name="clicked" handler="on_add_font_assigned_clicked" swapped="no"/>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="homogeneous">True</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToolButton" id="remove-font-assigned">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="tooltip_text" translatable="yes">Remove the font from the substitute font</property>
+                    <property name="label" translatable="yes">Remove the font from the substitute font</property>
+                    <property name="use_underline">True</property>
+                    <property name="icon_name">list-remove-symbolic</property>
+                    <signal name="clicked" handler="on_remove_font_assigned_clicked" swapped="no"/>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="homogeneous">True</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToolButton" id="move-up-subst">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="tooltip_text" translatable="yes">Move the selected font up</property>
+                    <property name="label" translatable="yes">Move the selected font up</property>
+                    <property name="use_underline">True</property>
+                    <property name="icon_name">go-up-symbolic</property>
+                    <signal name="clicked" handler="on_move_up_subst_clicked" swapped="no"/>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="homogeneous">True</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkToolButton" id="move-down-subst">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="tooltip_text" translatable="yes">Move the selected font down</property>
+                    <property name="label" translatable="yes">Move the selected font down</property>
+                    <property name="use_underline">True</property>
+                    <property name="icon_name">go-down-symbolic</property>
+                    <signal name="clicked" handler="on_move_down_subst_clicked" 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">1</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+        <child type="tab">
+          <object class="GtkLabel" id="label1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">page 1</property>
+          </object>
+          <packing>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkAlignment" id="alignment1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <child>
+              <object class="GtkLabel" id="label3">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">Please select a font.</property>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child type="tab">
+          <object class="GtkLabel" id="label2">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">page 2</property>
+          </object>
+          <packing>
+            <property name="position">1</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+      </object>
+      <packing>
+        <property name="expand">True</property>
+        <property name="fill">True</property>
+        <property name="position">1</property>
+      </packing>
+    </child>
+  </object>
+  <object class="GtkListStore" id="liststore">
+    <columns>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+    <data>
+      <row>
+        <col id="0" translatable="yes">Helvetica</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">Times</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">Courier</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">Arial</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">Times New Roman</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">Courier New</col>
+      </row>
+    </data>
+  </object>
+  <object class="GtkListStore" id="subst-fonts">
+    <columns>
+      <!-- column-name name -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+</interface>
 from fontstweak.util import FontsTweakUtil
 from fontstweak.aliasui import FontsTweakAliasUI
 from fontstweak.propui import FontsTweakPropUI
+from fontstweak.substui import FontsTweakSubstUI
 from fontstweak.langui import FontsTweakLangUI
 from gi.repository import Easyfc
 from gi.repository import FontsTweak
 
 aliasui_builder = FontsTweakUtil.create_builder('fonts-tweak-alias.ui')
 aliasui_builder.connect_signals(FontsTweakAliasUI(config, aliasui_builder, dlgui))
+aliasui = aliasui_builder.get_object('fonts-alias-ui')
+
 try:
     ezfcver = Easyfc.version()
     if LooseVersion(ezfcver) >= LooseVersion('0.8'):
 except AttributeError:
     propui = None
 
+try:
+    ezfcver = Easyfc.version()
+    if LooseVersion(ezfcver) >= LooseVersion('0.11'):
+        substui_builder = FontsTweakUtil.create_builder('fonts-tweak-subst.ui')
+        substui_builder.connect_signals(FontsTweakSubstUI(config, substui_builder, dlgui))
+        substui = substui_builder.get_object('fonts-subst-ui')
+except AttributeError:
+    substui = None
+
 langui_builder = FontsTweakUtil.create_builder('fonts-tweak-lang.ui')
 langui_obj = FontsTweakLangUI(langui_builder, dlgui)
 langui_builder.connect_signals(langui_obj)
-
-aliasui = aliasui_builder.get_object('fonts-alias-ui')
 if langui_obj.supported == True:
     langui = langui_builder.get_object('fonts-lang-order-ui')
 else:
 tabs.append_page(aliasui, Gtk.Label(_('Fonts Aliases')))
 if propui is not None:
     tabs.append_page(propui, Gtk.Label(_('Fonts Properties')))
+if substui is not None:
+    tabs.append_page(substui, Gtk.Label(_('Font Substitutions')))
 if langui is not None:
     tabs.append_page(langui, Gtk.Label(_('Language Ordering')))
 content_area.set_border_width(2)

fontstweak/chooserui.py

         self.filter.grab_focus()
         self._set_cursor()
 
+    def on_chooser_dialog_close(self, widget):
+        self.selector.unselect_all()
+
     def on_filter_activate(self, widget):
         self.add.clicked()
 

fontstweak/gresource.xml

     <file preprocess="xml-stripblanks">fonts-tweak-alias.ui</file>
     <file preprocess="xml-stripblanks">fonts-tweak-prop.ui</file>
     <file preprocess="xml-stripblanks">fonts-tweak-lang.ui</file>
+    <file preprocess="xml-stripblanks">fonts-tweak-subst.ui</file>
     <file preprocess="xml-stripblanks">chooser.ui</file>
   </gresource>
 </gresources>

fontstweak/substui.py

+# -*- coding: utf-8 -*-
+# substui.py
+# Copyright (C) 2012-2013 Red Hat, Inc.
+#
+# Authors:
+#   Akira TAGOH  <tagoh@redhat.com>
+#
+# This library 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 3 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import gettext
+import gi
+import re
+import sys
+from chooserui import ChooserUI
+from distutils.version import LooseVersion
+from util import FontsTweakUtil
+from gi.repository import Easyfc
+from gi.repository import GObject
+from gi.repository import Gtk
+
+_ = gettext.gettext
+
+class FontsTweakSubstUI:
+
+    def __init__(self, config, builder, parent):
+        self.__initialized = False
+
+        self.config = config
+        self.parent_window = parent
+        self.remove_subst_button = builder.get_object('remove-subst')
+        self.remove_font_button = builder.get_object('remove-font-assigned')
+        self.move_up_button = builder.get_object('move-up-subst')
+        self.move_down_button = builder.get_object('move-down-subst')
+        self.pages = builder.get_object('notebook-subst-pages')
+        self.subst_selector = builder.get_object('treeview-selection-subst')
+        self.assigned_selector = builder.get_object('treeview-selection-assigned')
+        self.subst_view = builder.get_object('treeview-subst-fonts-list')
+        self.assigned_view = builder.get_object('treeview-assigned-fonts-list')
+        self.subst_view.append_column(Gtk.TreeViewColumn(None, Gtk.CellRendererText(), text=0))
+        self.assigned_view.append_column(Gtk.TreeViewColumn(None, Gtk.CellRendererText(), text=0))
+        self.subst_view_list = builder.get_object('subst-fonts')
+        self.assigned_view_list = builder.get_object('assigned-fonts')
+        self.add_dialog = builder.get_object('add-subst-dialog')
+        self.add_dialog.set_transient_for(self.parent_window)
+        self.add_dialog.set_title(_('Select a font...'))
+        self.add_dialog_entry = builder.get_object('combobox-entry')
+        self.add_dialog_add_button = builder.get_object('button-add')
+
+        self.listobj = Gtk.ListStore(GObject.TYPE_STRING)
+        fonts = Easyfc.Font.get_list(None, None, False)
+        if len(fonts) == 0:
+            # fontconfig seems not supporting the namelang object
+            fonts = Easyfc.Font.get_list(None, None, True)
+        for f in fonts:
+            iter = self.listobj.append()
+            self.listobj.set_value(iter, 0, f)
+
+        chooser_builder = FontsTweakUtil.create_builder('chooser.ui')
+        chooser_builder.connect_signals(ChooserUI(chooser_builder, self.listobj, self.on_treemodel_filter))
+        self.chooser = chooser_builder.get_object('chooser-dialog')
+        self.chooser.set_transient_for(self.parent_window)
+        self.chooser.set_title(_('Select a font...'))
+        self.chooser_view = chooser_builder.get_object('treeview')
+        self.chooser_selector = chooser_builder.get_object('treeview-selection')
+        self.chooser_view.append_column(Gtk.TreeViewColumn(None, Gtk.CellRendererText(), text=0))
+
+        # check if current icon theme supports the symbolic icons
+        for i in ['add-subst', 'remove-subst', 'add-font-assigned', 'remove-font-assigned', 'move-up-subst', 'move-down-subst']:
+            icon = builder.get_object(i)
+            icon.set_icon_name(FontsTweakUtil.check_symbolic(icon.get_icon_name()))
+
+        for f in self.config.get_subst_family():
+            iter = self.subst_view_list.append()
+            self.subst_view_list.set_value(iter, 0, f)
+
+        self.on_treeview_selection_subst_changed(self.subst_selector)
+
+        self.__initialized = True
+
+    def on_treemodel_filter(self, model, iter, filter):
+        patterns = filter.get_text().split(' ')
+        if len(patterns) == 1 and patterns[0] == '':
+            return True
+        n = model.get_value(iter, 0)
+        for p in patterns:
+            if re.search(p, n, re.I):
+                return True
+
+        return False
+
+    def __treeview_selection_changed(self, widget, no_sel_cb, sel_cb, masked_buttons):
+        model, iter = widget.get_selected()
+        if iter == None:
+            no_sel_cb()
+            for w in masked_buttons:
+                w.set_sensitive(False)
+        else:
+            for w in masked_buttons:
+                w.set_sensitive(True)
+            sel_cb(model, iter)
+
+    def on_treeview_selection_subst_changed(self, widget):
+        self.__treeview_selection_changed(widget, lambda: self.pages.set_current_page(1),
+                                          lambda model, itr: self.pages.set_current_page(0),
+                                          [self.remove_subst_button])
+        smodel, siter = widget.get_selected()
+        if siter != None:
+            subst = smodel.get_value(siter, 0)
+            model = self.assigned_view.get_model()
+            model.clear()
+            for f in self.config.get_substs(subst):
+                iter = model.append()
+                model.set_value(iter, 0, f.get_family())
+                
+        self.on_treeview_selection_assigned_changed(self.assigned_selector)
+
+    def on_treeview_selection_assigned_changed(self, widget):
+        self.__treeview_selection_changed(widget, lambda: None,
+                                          lambda model, itr: None,
+                                          [self.remove_font_button, self.move_up_button, self.move_down_button])
+        model, iter = widget.get_selected()
+        if iter != None:
+            if model.iter_previous(iter) == None:
+                self.move_up_button.set_sensitive(False)
+            else:
+                self.move_up_button.set_sensitive(True)
+            if model.iter_next(iter) == None:
+                self.move_down_button.set_sensitive(False)
+            else:
+                self.move_down_button.set_sensitive(True)
+
+    def on_add_subst_clicked(self, widget):
+        self.add_dialog.show_all()
+        resid = self.add_dialog.run()
+        self.add_dialog.hide()
+        if resid == Gtk.ResponseType.CANCEL:
+            return
+        name = self.add_dialog_entry.get_text()
+        if name == None or len(name) == 0:
+            return
+        iter = self.add_subst(name)
+        if iter == None:
+            print "%s has already been added." % name
+        else:
+            model = self.subst_view.get_model()
+            path = model.get_path(iter)
+            self.subst_view.set_cursor(path, None, False)
+
+    def on_remove_subst_clicked(self, widget):
+        model, iter = self.subst_selector.get_selected()
+        if iter == None:
+            return
+        n = model.get_value(iter, 0)
+        self.config.remove_substs(n)
+        try:
+            self.config.save()
+        except gi._glib.GError, e:
+            if e.domain != 'ezfc-error-quark' and e.code != 6:
+                raise
+        model.remove(iter)
+        self.on_treeview_selection_subst_changed(self.subst_selector)
+
+    def on_add_font_assigned_clicked(self, widget):
+        self.chooser.show_all()
+        resid = self.chooser.run()
+        self.chooser.hide()
+        if resid == Gtk.ResponseType.CANCEL:
+            return
+        model, iter = self.chooser_selector.get_selected()
+        if iter == None:
+            return
+        font = model.get_value(iter, 0)
+        iter = self.add_font(font)
+        if iter == None:
+            print "%s has already been added." % font
+        else:
+            model = self.assigned_view.get_model()
+            path = model.get_path(iter)
+            self.assigned_view.set_cursor(path, None, False)
+
+    def on_remove_font_assigned_clicked(self, widget):
+        smodel, siter = self.subst_selector.get_selected()
+        if siter == None:
+            return
+        subst = smodel.get_value(siter, 0)
+        model, iter = self.assigned_selector.get_selected()
+        if iter == None:
+            return
+        font = model.get_value(iter, 0)
+        self.config.remove_subst(subst, font)
+        try:
+            self.config.save()
+        except gi._glib.GError, e:
+            if e.domain != 'ezfc-error-quark' and e.code != 6:
+                raise
+        model.remove(iter)
+        self.on_treeview_selection_assigned_changed(self.assigned_selector)
+
+    def __update_subst_order(self):
+        smodel, siter = self.subst_selector.get_selected()
+        if siter == None:
+            return
+        subst = smodel.get_value(siter, 0)
+        model = self.assigned_view.get_model()
+        iter = model.get_iter_first()
+        a = []
+        while iter != None:
+            f = model.get_value(iter, 0)
+            a.append(f)
+            iter = model.iter_next(iter)
+        self.config.remove_substs(subst)
+        for f in a:
+            o = Easyfc.Font()
+            o.add_family(f)
+            self.config.add_subst(subst, o)
+        try:
+            self.config.save()
+        except gi._glib.GError, e:
+            if e.domain != 'ezfc-error=quark' and e.code != 6:
+                raise
+
+    def on_move_up_subst_clicked(self, widget):
+        model, iter = self.assigned_selector.get_selected()
+        piter = model.iter_previous(iter)
+        if piter == None:
+            return
+        f = model.get_value(iter, 0)
+        niter = model.insert_before(piter)
+        model.set_value(niter, 0, f)
+        model.remove(iter)
+        path = model.get_path(niter)
+        self.assigned_view.set_cursor(path, None, False)
+        self.__update_subst_order()
+
+    def on_move_down_subst_clicked(self, widget):
+        model, iter = self.assigned_selector.get_selected()
+        niter = model.iter_next(iter)
+        if niter == None:
+            return
+        f = model.get_value(iter, 0)
+        newiter = model.insert_after(niter)
+        model.set_value(newiter, 0, f)
+        model.remove(iter)
+        path = model.get_path(newiter)
+        self.assigned_view.set_cursor(path, None, False)
+        self.__update_subst_order()
+
+    def on_add_subst_dialog_close(self, widget):
+        self.add_dialog_entry.set_text('')
+
+    def on_add_subst_dialog_show(self, widget):
+        self.add_dialog_entry.set_text('')
+        self.add_dialog_entry.grab_focus()
+
+    def add_subst(self, font):
+        retval = True
+        model = self.subst_view.get_model()
+        iter = model.get_iter_first()
+        while iter != None:
+            f = model.get_value(iter, 0)
+            if f == font:
+                retval = False
+                break
+            iter = model.iter_next(iter)
+        if retval == True:
+            iter = model.append()
+            model.set_value(iter, 0, font)
+        else:
+            iter = None
+        return iter
+
+    def add_font(self, font):
+        retval = True
+        model = self.assigned_view.get_model()
+        iter = model.get_iter_first()
+        while iter != None:
+            f = model.get_value(iter, 0)
+            if f == font:
+                retval = False
+                break
+            iter = model.iter_next(iter)
+        if retval == True:
+            smodel, siter = self.subst_selector.get_selected()
+            if siter == None:
+                return
+            iter = model.append()
+            model.set_value(iter, 0, font)
+            subst = smodel.get_value(siter, 0)
+            o = Easyfc.Font()
+            o.add_family(font)
+            self.config.add_subst(subst, o)
+            try:
+                self.config.save()
+            except gi._glib.GError, e:
+                if e.domain != 'ezfc-error-quark' and e.code != 6:
+                    raise
+        else:
+            iter = None
+        return iter
 [type: gettext/glade]data/fonts-tweak-alias.ui
 [type: gettext/glade]data/fonts-tweak-lang.ui
 [type: gettext/glade]data/fonts-tweak-prop.ui
+[type: gettext/glade]data/fonts-tweak-subst.ui
 fonts-tweak-tool.desktop.in
 fontstweak/aliasui.py
 fontstweak/chooserui.py
 fontstweak/fontstweak.py
 fontstweak/langui.py
 fontstweak/propui.py
+fontstweak/substui.py
 fontstweak/util.py

po/fonts-tweak-tool.pot

 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-01-22 19:39+0900\n"
+"POT-Creation-Date: 2013-02-15 19:37+0900\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 msgstr ""
 
 #: ../data/fonts-tweak-alias.ui.h:3
-msgid "Do not filter the font family name by classification."
+msgid "Do not filter a font family name by classification"
 msgstr ""
 
 #: ../data/fonts-tweak-alias.ui.h:4
-msgid "Displays the localized family name if available."
+msgid "List a localized family name if available"
 msgstr ""
 
 #: ../data/fonts-tweak-alias.ui.h:5 ../data/fonts-tweak-prop.ui.h:9
 msgstr ""
 
 #: ../data/fonts-tweak-prop.ui.h:7
-msgid "Use the sub-pixel rendering"
+msgid "Use sub-pixel rendering"
 msgstr ""
 
 #: ../data/fonts-tweak-prop.ui.h:8
-msgid "Use the embedded bitmap font if any"
+msgid "Use embedded bitmap font if any"
 msgstr ""
 
 #: ../data/fonts-tweak-prop.ui.h:10
 msgid "Tweak fonts by language using fontconfig"
 msgstr ""
 
+#: ../fontstweak/aliasui.py:92 ../fontstweak/langui.py:68
+msgid "Choose a language..."
+msgstr ""
+
+#: ../fontstweak/propui.py:82
+msgid "Select a font..."
+msgstr ""
+
 #: ../fontstweak/util.py:55
 msgid "Default"
 msgstr ""