OSDN Git Service

Cleanup, refactoring and bug fixes.
authorShyouzou Sugitani <shy@users.sourceforge.jp>
Sun, 4 Sep 2011 12:26:39 +0000 (21:26 +0900)
committerShyouzou Sugitani <shy@users.sourceforge.jp>
Sun, 4 Sep 2011 12:26:39 +0000 (21:26 +0900)
19 files changed:
ChangeLog
MANIFEST.in [new file with mode: 0644]
lib/main.py
lib/ninix/alias.py
lib/ninix/balloon.py
lib/ninix/communicate.py
lib/ninix/config.py
lib/ninix/dll.py
lib/ninix/dll/bln.py
lib/ninix/install.py
lib/ninix/kinoko.py
lib/ninix/menu.py
lib/ninix/nekodorif.py
lib/ninix/pix.py
lib/ninix/sakura.py
lib/ninix/script.py
lib/ninix/seriko.py
lib/ninix/surface.py
lib/ninix/version.py

index 1d6f13d..cf77f61 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,22 @@
+Sun September 4 2011   Shyouzou Sugitani <shy@users.sourceforge.jp>
+       * Observer関連のメソッドを修正した.
+         (SakuraクラスでのObserverの管理データはlist型からdict型に変更.)
+       * アイコン化解除イベントが間違って発行される場合があったのを修正した.
+
+Sat September 3 2011   Shyouzou Sugitani <shy@users.sourceforge.jp>
+       * コード全体をリファクタリング.
+         - New-style classの使用を拡大.(property使用を増やした.)
+         - リストの内包表記を積極的に使用.
+       * setup.pyのためにMANIFEST.inを追加した.
+       *lib/main.py: 変数名の変更忘れがあったのを修正.
+       *install.py:
+         コンソールからバルーンをインストールすると落ちる問題を修正.
+       * \n[half]を使用するとバルーンの表示がおかしくなることがあったのを修正.
+         (高さが半分に変更された行がスクロールで表示範囲外に出た後に
+          表示領域内の行の位置を修正する処理が抜けていた.)
+       * easyballoon互換モジュールで表示されるバルーンもサーフェスの
+         倍率変更に即追従するようにした.
+
 Sun August 28 2011   Shyouzou Sugitani <shy@users.sourceforge.jp>
        * バージョン4.2.5リリース.
 
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644 (file)
index 0000000..99eda40
--- /dev/null
@@ -0,0 +1,4 @@
+include COPYING KNOWN_ISSUES ChangeLog NEWS README README.ninix TODO.ninix
+recursive-include doc *.txt
+recursive-include locale *.po
+prune bin
index 488c757..596eca6 100755 (executable)
@@ -765,14 +765,13 @@ class Application(object):
         if ghost_conf:
             self.ghosts[ghost_dir] = ghost_conf[ghost_dir]
         else:
-            self.ghosts[i] = None ## FIXME
+            self.ghosts[ghost_dir] = None ## FIXME
         item = self.__create_ghost_list_item(ghost_dir)
         item['instance'] = self.create_ghost(ghost_conf[ghost_dir])
         if exists:
             self.ghost_list[num] = item
         else:
             self.ghost_list.insert(num, item)
-        self.console.update() # XXX
 
     def vanish_sakura(self, sakura):
         # remove ghost
@@ -938,17 +937,21 @@ class Console:
             self.app.quit()
         return True
 
+    def do_install(self, filename):
+        try:
+            target_dir = self.installer.install(
+                filename, ninix.home.get_ninix_home())
+        except:
+            target_dir = None
+        if target_dir is not None:
+            self.app.add_sakura(target_dir)
+            self.update()
+
     def file_chooser(self, widget=None, event=None):
         response = self.file_chooser.run()
         if response == gtk.RESPONSE_OK:
             filename = self.file_chooser.get_filename()
-            try:
-                target_dir = self.installer.install(
-                    filename, ninix.home.get_ninix_home())
-            except:
-                target_dir = None
-            if target_dir is not None:
-                self.app.add_sakura(target_dir)
+            self.do_install(filename)
         elif response == gtk.RESPONSE_CANCEL:
             pass
         self.file_chooser.hide()
@@ -1004,13 +1007,7 @@ class Console:
                 if scheme == 'file' and os.path.exists(pathname):
                     filelist.append(pathname)
             for filename in filelist:
-                try:
-                    target_dir = self.installer.install(
-                        filename, ninix.home.get_ninix_home())
-                except:
-                    target_dir = None
-                if target_dir is not None:
-                    self.app.add_sakura(target_dir)
+                self.do_install(filename)
         return True
 
 
@@ -1042,9 +1039,9 @@ class UsageDialog:
         if self.opened:
             return
         self.history = history
-        self.items = []
-        for name, (clock, path) in self.history.iteritems():
-            self.items.append((name, clock, path))
+        self.items = \
+            [(name, clock, path) for name, (clock, path) in \
+                 self.history.iteritems()]
         self.items[:] = [(x[1], x) for x in self.items]
         self.items.sort()
         self.items[:] = [x for x_1, x in self.items]
index 22ca9d3..aac6852 100644 (file)
@@ -22,13 +22,8 @@ def fatal(error):
     return ninix.config.null_config()
 
 def create_from_file(path):
-    buf = []
     with open(path) as f:
-        for line in f:
-            line = line.strip()
-            if not line:
-                continue
-            buf.append(line)
+        buf = [line.strip() for line in f if line.strip()]
     return create_from_buffer(buf)
 
 def create_from_buffer(buf):
index 0048242..5e1b5c9 100644 (file)
@@ -26,10 +26,6 @@ class Balloon:
     def __init__(self, callback):
         self.callback = callback
         self.synchronized = []
-        self.__scalling = 0
-        self.__scale = 100 # %
-        self.__use_pna = False
-        self.__alpha_channel = 1.0
         self.user_interaction = False
         self.window = []
         # create communicatebox
@@ -70,38 +66,9 @@ class Balloon:
         if len(self.window) > side:
             self.window[side].reset_text_count()
 
-    def set_use_pna(self, flag):
-        if self.__use_pna == bool(flag):
-            return
-        self.__use_pna = bool(flag)
-        for balloon_window in self.window:
-            balloon_window.set_use_pna(self.__use_pna)
-
-    def set_alpha_channel(self, alpha):
-        if not 0.1 <= alpha <= 1.0 or alpha is None:
-            alpha = 1.0
-        if self.__alpha_channel == alpha:
-            return
-        self.__alpha_channel = alpha
-        for balloon_window in self.window:
-            balloon_window.set_alpha_channel(self.__alpha_channel)
-
-    def set_scale(self, scale):
-        if self.__scale == scale:
-            return
-        self.__scale = scale # %
-        for balloon_window in self.window:
-            balloon_window.set_scale(self.__scale)
-
-    def get_scalling(self):
-        return self.__scalling
-
-    def set_scalling(self, flag):
-        if self.__scalling == flag:
-            return
-        self.__scalling = flag
+    def reset_balloon(self):
         for balloon_window in self.window:
-            balloon_window.set_scalling(self.__scalling)
+            balloon_window.reset_balloon()
 
     def create_gtk_window(self, title):
         window = ninix.pix.TransparentWindow()
@@ -194,19 +161,10 @@ class Balloon:
             gtk_window, side, balloon_window_callback, self.desc, balloon,
             id_format)
         self.window.append(balloon_window)
-        # BalloonWindow default should be same as Balloon default
-        balloon_window.set_scalling(self.__scalling)
-        balloon_window.set_scale(self.__scale)
-        balloon_window.set_use_pna(self.__use_pna)
-        balloon_window.set_alpha_channel(self.__alpha_channel)
-        balloon_window.set_fonts(self.callback['get_preference']('balloon_fonts'))
-        if side > 1:
-            balloon_window.set_balloon(0) ## FIXME
 
     def reset_fonts(self):
-        font_name = self.callback['get_preference']('balloon_fonts')
         for window in self.window:
-            window.set_fonts(font_name)
+            window.reset_fonts()
 
     def get_balloon_directory(self):
         return self.directory
@@ -378,7 +336,7 @@ class Balloon:
             self.inputbox.show(default)
 
 
-class BalloonWindow:
+class BalloonWindow(object):
 
     def __init__(self, window, side, callback, desc, balloon, id_format):
         self.window = window
@@ -396,10 +354,6 @@ class BalloonWindow:
         self.images = []
         self.width = 0
         self.height = 0
-        self.__scalling = 0
-        self.__scale = 100 # %
-        self.__use_pna = False
-        self.__alpha_channel = 1.0
         self.__font_name = ''
         self.text_count = 0
         self.balloon_pixbuf = None
@@ -435,8 +389,15 @@ class BalloonWindow:
         # initialize
         self.direction = min(side, 1) ## kluge: multi character
         self.position = (0, 0)
+        self.reset_fonts()
         self.clear_text()
 
+    @property
+    def scale(self):
+        scalling = self.callback['get_preference']('balloon_scalling')
+        scale = self.callback['get_preference']('surface_scale')
+        return scale if scalling else 100 # %
+
     def get_balloon_windowposition(self):
         winpos_x = self.__get_with_scaling('windowposition.x', int, 0)
         winpos_y = self.__get_with_scaling('windowposition.y', int, 0)
@@ -446,11 +407,12 @@ class BalloonWindow:
         #assert balloon_id in self.balloon
         try:
             path, config = self.balloon[balloon_id]
+            use_pna = self.callback['get_preference']('use_pna')
             pixbuf = ninix.pix.create_pixbuf_from_file(
-                path, use_pna=self.__use_pna)
+                path, use_pna=use_pna)
         except:
             return None
-        scale = self.get_scale()
+        scale = self.scale
         w = pixbuf.get_width()
         h = pixbuf.get_height()
         w = max(8, int(w * scale / 100))
@@ -458,11 +420,8 @@ class BalloonWindow:
         pixbuf = pixbuf.scale_simple(w, h, gtk.gdk.INTERP_BILINEAR)
         return pixbuf, (w, h)
 
-    def set_use_pna(self, flag):
-        self.__use_pna = flag
-        self.reset_balloon()
-
-    def set_fonts(self, font_name):
+    def reset_fonts(self):
+        font_name = self.callback['get_preference']('balloon_fonts')
         if self.__font_name == font_name:
             return
         self.font_desc = pango.FontDescription(font_name)
@@ -473,14 +432,14 @@ class BalloonWindow:
                 ['font.height', 'font.size'], int, default_size)
             pango_size = size * 3 / 4 # convert from Windows to GTK+
             pango_size *= pango.SCALE
-        scale = self.get_scale()
+        scale = self.scale
         self.font_desc.set_size(pango_size * scale / 100)
         self.__font_name = font_name
         self.layout.set_font_description(self.font_desc)
         self.layout.set_wrap(pango.WRAP_CHAR) # XXX
         # font for sstp message
         if self.side == 0:
-            self.sstp_font_desc = pango.FontDescription(self.__font_name)
+            self.sstp_font_desc = pango.FontDescription(font_name)
             pango_size = self.sstp_font_desc.get_size()
             if pango_size == 0:
                 default_size = 10 # for Windows environment
@@ -496,20 +455,12 @@ class BalloonWindow:
             if self.__shown:
                 self.darea.queue_draw()
 
-    def set_alpha_channel(self, value):
-        self.__alpha_channel = value
-        self.reset_balloon()
-
-    def get_scale(self):
-        return self.__scale if self.__scalling else 100 # %
-
-    def set_scale(self, scale):
-        self.__scale = scale # %
-        self.reset_balloon()
-
-    def set_scalling(self, flag):
-        self.__scalling = flag
-        self.reset_balloon()
+    @property
+    def alpha_channel(self):
+        alpha = self.callback['get_preference']('balloon_alpha')
+        if alpha is None or not 0.1 <= alpha <= 1.0:
+            alpha = 1.0
+        return alpha
 
     def reset_sstp_marker(self):
         if self.side == 0:
@@ -656,7 +607,7 @@ class BalloonWindow:
             value = default_value
         if value < 0:
             value = base + value
-        return int(value * self.get_scale() / 100)
+        return int(value * self.scale / 100)
 
     def __get_with_scaling(self, name, conv, default_value):
         path, config = self.balloon[self.balloon_id]
@@ -665,13 +616,15 @@ class BalloonWindow:
             value = self.desc.get_with_type(name, conv)
             if value is None:
                 value = default_value
-        return conv(value * self.get_scale() / 100)
+        return conv(value * self.scale / 100)
 
     def __move(self):
         x, y = self.get_position()
         self.window.resize_move(x, y)
 
     def set_position(self, base_x, base_y):
+        if self.balloon_id is None: ## FIXME
+            return
         px, py = self.get_balloon_windowposition()
         if self.direction == 0:
             w, h = self.get_balloon_size()
@@ -823,11 +776,11 @@ class BalloonWindow:
         cr.paint()
         cr.restore()
         cr.set_source_pixbuf(self.balloon_pixbuf[0], 0, 0)
-        cr.paint_with_alpha(self.__alpha_channel)
+        cr.paint_with_alpha(self.alpha_channel)
         # draw images
         for i in range(len(self.images)):
             pixbuf, (w, h), (x, y) = self.images[i]
-            scale = self.get_scale()
+            scale = self.scale
             w = max(8, int(w * scale / 100))
             h = max(8, int(h * scale / 100))
             if x == 'centerx':
@@ -848,7 +801,7 @@ class BalloonWindow:
                     continue
             pixbuf = pixbuf.scale_simple(w, h, gtk.gdk.INTERP_BILINEAR)
             cr.set_source_pixbuf(pixbuf, x, y)
-            cr.paint_with_alpha(self.__alpha_channel)
+            cr.paint_with_alpha(self.alpha_channel)
         # draw text
         i = self.lineno
         j = len(self.text_buffer)
@@ -859,18 +812,15 @@ class BalloonWindow:
             x, y, w, h = self.line_regions[line]
             if self.text_buffer[i].endswith('\n[half]'):
                 new_y = int(y + (self.font_height + self.line_space) / 2)
-                self.update_line_regions(line + 1, new_y)
                 markup = self.set_markup(i, self.text_buffer[i][:-7])
-                self.layout.set_markup(markup)
-                cr.set_source_rgba(*self.text_normal_color)
-                cr.move_to(x, y)
-                cr.show_layout(self.layout)
             else:
+                new_y = int(y + self.font_height + self.line_space)
                 markup = self.set_markup(i, self.text_buffer[i])
-                self.layout.set_markup(markup)
-                cr.set_source_rgba(*self.text_normal_color)
-                cr.move_to(x, y)
-                cr.show_layout(self.layout)
+            self.update_line_regions(line + 1, new_y)
+            self.layout.set_markup(markup)
+            cr.set_source_rgba(*self.text_normal_color)
+            cr.move_to(x, y)
+            cr.show_layout(self.layout)
             if self.sstp_pixbuf is not None:
                 for l, c in self.sstp_marker:
                     if l == i:
@@ -881,7 +831,7 @@ class BalloonWindow:
                         my = y + (self.font_height + self.line_space) / 2
                         my = my - mh / 2
                         cr.set_source_pixbuf(self.sstp_pixbuf[0], mx, my)
-                        cr.paint_with_alpha(self.__alpha_channel)
+                        cr.paint_with_alpha(self.alpha_channel)
             i += 1
             line += 1
         del cr
index 3be787b..2e16831 100644 (file)
@@ -27,11 +27,8 @@ class Communicate:
             self.__ghosts[sakura] = (name, s0, s1)
 
     def get_otherghostname(self, name):
-        otherghostname = []
-        for value in self.__ghosts.itervalues():
-            if value[0] != name:
-                otherghostname.append(chr(1).join(value))
-        return otherghostname
+        return [chr(1).join(value) for value in self.__ghosts.itervalues() \
+                    if value[0] != name]
 
     def send_message(self, name, sender, sentence):
         if name == '__SYSTEM_ALL_GHOST__':
index aff47e2..e7dfbe9 100644 (file)
@@ -35,19 +35,12 @@ class Config(dict):
         return default
 
     def __str__(self):
-        buf = []
-        for item in self.iteritems():
-            buf.append('%s,%s\n' % item)
-        return ''.join(buf)
+        return ''.join(['%s,%s\n' % item for item in self.iteritems()])
 
 
 def create_from_file(path):
-    buf = []
     with open(path) as f:
-        for line in f:
-            if not line.strip():
-                continue
-            buf.append(line)
+        buf = [line.strip() for line in f if line.strip()]
     return create_from_buffer(buf)
 
 def create_from_buffer(buf):
index 409393c..17f076e 100644 (file)
@@ -122,36 +122,34 @@ class Library:
         name = name.replace('\\', '/')
         head, tail = os.path.split(name)
         name = tail
-        if name:
-            if name.lower().endswith('.dll'): # XXX
-                name = name[:-4]
-        else:
+        if not name:
             return None
+        if name.lower().endswith('.dll'): # XXX
+            name = name[:-4]
         module = self.__import_module(name)
         if not module:
             return None
-        else:
-            instance = None
-            if self.__type == 'saori':
-                if getattr(module, 'Saori', None):
-                    saori = module.Saori()
-                    if getattr(saori, 'need_ghost_backdoor', None):
-                        saori.need_ghost_backdoor(self.__sakura)
-                else:
-                    saori = None
-                instance = saori
-            elif self.__type == 'shiori':
-                if getattr(module, 'Shiori', None):
-                    shiori = module.Shiori(dll_name)
-                    if getattr(shiori, 'use_saori', None):
-                        shiori.use_saori(self.__saori_lib)
-                else:
-                    shiori = None
-                instance = shiori
-            if instance is None:
-                del module
-                del sys.modules[name]
-            return instance
+        instance = None
+        if self.__type == 'saori':
+            if getattr(module, 'Saori', None):
+                saori = module.Saori()
+                if getattr(saori, 'need_ghost_backdoor', None):
+                    saori.need_ghost_backdoor(self.__sakura)
+            else:
+                saori = None
+            instance = saori
+        elif self.__type == 'shiori':
+            if getattr(module, 'Shiori', None):
+                shiori = module.Shiori(dll_name)
+                if getattr(shiori, 'use_saori', None):
+                    shiori.use_saori(self.__saori_lib)
+            else:
+                shiori = None
+            instance = shiori
+        if instance is None:
+            del module
+            del sys.modules[name]
+        return instance
 
     def __import_module(self, name):
         fp = None
index f6f6599..1c4449a 100644 (file)
@@ -46,7 +46,11 @@ class Saori(SAORI):
 
     def setup(self):
         self.blns = self.read_bln_txt(self.dir)
-        return 1 if self.blns else 0
+        if self.blns:
+            self.__sakura.attach_observer(self)
+            return 1
+        else:
+            return 0
 
     def read_bln_txt(self, dir):
         blns = {}
@@ -87,8 +91,17 @@ class Saori(SAORI):
                     bln[bln_id].destroy()
                     del bln[bln_id]
         self.blns = {}
+        self.__sakura.detach_observer(self)
         return 1
 
+    def observer_update(self, event, args): ## FIXME
+        if event == 'set scale':
+            for name in self.blns:
+                data, bln = self.blns[name]
+                for bln_id in bln.keys():
+                    if bln[bln_id]:
+                        bln[bln_id].reset_scale()
+
     def execute(self, argument):
         if not argument:
             return self.RESPONSE[400]
@@ -136,7 +149,8 @@ class Saori(SAORI):
                     self.blns[name] = (data, {})
         return None
 
-class Balloon:
+
+class Balloon(object):
 
     def __init__(self, sakura, dir, data,
                  text, offset_x, offset_y, name, bln_id):
@@ -179,32 +193,27 @@ class Balloon:
         except:
             self.destroy()
             return
-        self.scale = self.get_sakura_status('SurfaceScale') ## FIXME
+        self.balloon_pixbuf = balloon_pixbuf
         w = balloon_pixbuf.get_width()
         h = balloon_pixbuf.get_height()
-        w = max(8, int(w * self.scale / 100))
-        h = max(8, int(h * self.scale / 100))
-        balloon_pixbuf = balloon_pixbuf.scale_simple(
-            w, h, gtk.gdk.INTERP_BILINEAR)
-        mask_pixmap = gtk.gdk.Pixmap(None, w, h, 1)
-        balloon_pixbuf.render_threshold_alpha(
-            mask_pixmap, 0, 0, 0, 0, w, h, 1)
-        self.x, self.y = self.get_coordinate(w, h)
+        self.x = self.y = 0
         if 'offset.x' in data:
-            self.x += self.direction * int(int(data['offset.x']) * self.scale / 100)
+            self.x += self.direction * int(data['offset.x'])
         if 'offset.y' in data:
-            self.y += int(int(data['offset.y']) * self.scale / 100)
+            self.y += int(data['offset.y'])
         if 'offset.random' in data:
-            self.x += int(int(data['offset.random']) * random.randrange(-1, 2) * self.scale / 100)
-            self.y += int(int(data['offset.random']) * random.randrange(-1, 2) * self.scale / 100)
-        self.x += self.direction * int(offset_x * self.scale / 100)
-        self.y += int(offset_y * self.scale / 100)
-        self.left = int(int(data.get('disparea.left', 0)) * self.scale / 100)
-        self.right = int(int(data.get('disparea.right', w)) * self.scale / 100)
-        self.top = int(int(data.get('disparea.top', 0)) * self.scale / 100)
-        self.bottom = int(int(data.get('disparea.bottom', h)) * self.scale / 100)
-        self.window.mask = mask_pixmap
-        self.current_balloon_pixbuf = balloon_pixbuf
+            self.x += int(data['offset.random']) * random.randrange(-1, 2)
+            self.y += int(data['offset.random']) * random.randrange(-1, 2)
+        self.x += self.direction * int(offset_x)
+        self.y += int(offset_y)
+        self.action_x = 0
+        self.action_y = 0
+        self.vx = 0
+        self.vy = 0
+        self.left = int(data.get('disparea.left', 0))
+        self.right = int(data.get('disparea.right', w))
+        self.top = int(data.get('disparea.top', 0))
+        self.bottom = int(data.get('disparea.bottom', h))
         self.script = None
         self.darea = gtk.DrawingArea()
         self.darea.set_events(gtk.gdk.EXPOSURE_MASK|
@@ -217,13 +226,13 @@ class Balloon:
         self.darea.connect('button_release_event', self.button_release)
         self.darea.connect('motion_notify_event', self.motion_notify)
         self.darea.connect('leave_notify_event', self.leave_notify)
-        self.darea.set_size_request(w, h)
         self.darea.show()
         self.window.add(self.darea)
         self.darea.realize()
         self.window.realize()
         self.window.window.set_back_pixmap(None, False)
-        self.window.resize_move(self.x, self.y)
+        self.set_skin()
+        self.set_position()
         self.layout = None
         if text != 'noscript' and \
            (self.right - self.left) and (self.bottom - self.top):
@@ -234,20 +243,16 @@ class Balloon:
                 fontcolor_b = int(data['font.color'][4:6], 16) / 255.0
                 self.fontcolor = (fontcolor_r, fontcolor_g, fontcolor_b)
             default_font_size = 12 # for Windows environment
-            font_size = int(data.get('font.size', default_font_size))
-            font_size = font_size * 3 / 4 # convert from Windows to GTK+
-            font_size = font_size * self.scale / 100 * pango.SCALE
+            self.font_size = int(data.get('font.size', default_font_size))
             self.layout = pango.Layout(self.darea.get_pango_context())
             self.font_desc = pango.FontDescription()
             self.font_desc.set_family('Sans') # FIXME
-            self.font_desc.set_size(font_size)
             if data.get('font.bold') == 'on':
                 self.font_desc.set_weight(pango.WEIGHT_BOLD)
-            self.layout.set_font_description(self.font_desc)
             self.layout.set_wrap(pango.WRAP_CHAR)
-            self.layout.set_width((self.right - self.left) * 1024)
-        self.slide_vx = int(int(data.get('slide.vx', 0)) * self.scale / 100)
-        self.slide_vy = int(int(data.get('slide.vy', 0)) * self.scale / 100)
+            self.set_layout()
+        self.slide_vx = int(data.get('slide.vx', 0))
+        self.slide_vy = int(data.get('slide.vy', 0))
         self.slide_autostop = int(data.get('slide.autostop', 0))
         if data.get('action.method') in ['sinwave', 'vibrate']:
             action = data['action.method']
@@ -289,12 +294,6 @@ class Balloon:
         self.visible = 0
         self.x_root = None
         self.y_root = None
-        self.action_x = 0
-        self.action_y = 0
-        self.vx = 0
-        self.vy = 0
-        self.text_pos_x = self.left
-        self.text_pos_y = self.top
         self.processed_script = None
         self.processed_text = ''
         self.text = ''
@@ -310,12 +309,61 @@ class Balloon:
             logging.error(self.script)
         self.timeout_id = glib.timeout_add(10, self.do_idle_tasks)
 
+    def set_position(self):
+        if self.window is None:
+            return
+        new_x = self.base_x + int((self.x + self.action_x + self.vx) * self.scale / 100)
+        new_y = self.base_y + int((self.y + self.action_y + self.vy) * self.scale / 100)
+        self.window.resize_move(new_x, new_y)
+
+    def set_skin(self):
+        if self.window is None:
+            return
+        self.scale = self.get_sakura_status('SurfaceScale') ## FIXME
+        balloon_pixbuf = self.balloon_pixbuf
+        w = balloon_pixbuf.get_width()
+        h = balloon_pixbuf.get_height()
+        w = max(8, int(w * self.scale / 100))
+        h = max(8, int(h * self.scale / 100))
+        balloon_pixbuf = balloon_pixbuf.scale_simple(
+            w, h, gtk.gdk.INTERP_BILINEAR)
+        mask_pixmap = gtk.gdk.Pixmap(None, w, h, 1)
+        balloon_pixbuf.render_threshold_alpha(
+            mask_pixmap, 0, 0, 0, 0, w, h, 1)
+        self.window.mask = mask_pixmap
+        self.current_balloon_pixbuf = balloon_pixbuf
+        self.darea.set_size_request(w, h)
+        self.base_x, self.base_y = self.get_coordinate(w, h)
+
+    def set_layout(self):
+        if self.window is None:
+            return
+        if self.layout is None:
+            return
+        font_size = self.font_size * 3 / 4 # convert from Windows to GTK+
+        font_size = font_size * self.scale / 100 * pango.SCALE
+        self.font_desc.set_size(font_size)
+        self.layout.set_font_description(self.font_desc)
+        self.layout.set_width(
+            int((self.right - self.left) * 1024 * self.scale / 100))
+
+    def reset_scale(self):
+        if self.window is None:
+            return
+        self.set_skin()
+        self.set_position()
+        self.set_layout()
+        self.darea.queue_draw()
+
+    @property
     def clickerase(self):
         return self.data.get('clickerase', 'on') == 'on'
 
+    @property
     def dragmove_horizontal(self):
         return self.data.get('dragmove.horizontal') == 'on'
 
+    @property
     def dragmove_vertical(self):
         return self.data.get('dragmove.vertical') == 'on'
 
@@ -492,27 +540,26 @@ class Balloon:
                                                 % self.action['ref2'])
                                         / self.action['ref2'])
                     if self.action['ref0']:
-                        self.action_y = int(offset * self.scale / 100)
+                        self.action_y = int(offset)
                     else:
-                        self.action_x = int(offset * self.scale / 100)
+                        self.action_x = int(offset)
                 elif self.action['method'] == 'vibrate':
                     offset = (int((time.time() - self.start_time) * 1000) / \
                               self.action['ref2']) % 2
-                    self.action_x = int(offset * self.action['ref0'] * self.scale / 100)
-                    self.action_y = int(offset * self.action['ref1'] * self.scale / 100)
+                    self.action_x = int(offset * self.action['ref0'])
+                    self.action_y = int(offset * self.action['ref1'])
             if (self.slide_vx != 0 or self.slide_vy != 0) and \
                    self.slide_autostop > 0 and \
                    self.slide_autostop * 0.001 + 0.05 <= time.time() - self.start_time:
-                self.vx = self.direction * int((self.slide_autostop / 50.0 + 1) * self.slide_vx * self.scale / 100)
+                self.vx = self.direction * int((self.slide_autostop / 50.0 + 1) * self.slide_vx)
                 self.slide_vx = 0
-                self.vy = int((self.slide_autostop / 50.0 + 1) * self.slide_vy * self.scale / 100)
+                self.vy = int((self.slide_autostop / 50.0 + 1) * self.slide_vy)
                 self.slide_vy = 0
             if self.slide_vx != 0:
-                self.vx = self.direction * int(((time.time() - self.start_time) * self.slide_vx * self.scale / 100) / 50 * 1000.)
+                self.vx = self.direction * int(((time.time() - self.start_time) * self.slide_vx) / 50 * 1000.)
             if self.slide_vy != 0:
-                self.vy = int(((time.time() - self.start_time) * self.slide_vy * self.scale / 100) / 50 * 1000.)
-            self.window.resize_move(int(self.x + self.action_x + self.vx),
-                                    int(self.y + self.action_y + self.vy))
+                self.vy = int(((time.time() - self.start_time) * self.slide_vy) / 50 * 1000.)
+            self.set_position()
             if self.processed_script or self.processed_text:
                 self.interpret_script()
         self.talking = 0 if self.talking and not sakura_talking else sakura_talking
@@ -529,7 +576,8 @@ class Balloon:
         cr.paint()
         if self.layout:
             cr.set_source_rgb(*self.fontcolor)
-            cr.move_to(self.left, self.top)
+            cr.move_to(int(self.left * self.scale / 100),
+                       int(self.top * self.scale / 100))
             cr.show_layout(self.layout)
         del cr
 
@@ -601,8 +649,8 @@ class Balloon:
         if event.type == gtk.gdk._2BUTTON_PRESS:
             x = int(event.x * 100 / self.scale)
             y = int(event.y * 100 / self.scale)
-            self.__sakura.notify_event('OnEBMouseDoubleClick', self.name,
-                                       x, y, self.id)
+            self.__sakura.notify_event(
+                'OnEBMouseDoubleClick', self.name, x, y, self.id)
         return True
 
     def button_release(self, widget, event):
@@ -612,35 +660,35 @@ class Balloon:
         y = int(event.y * 100 / self.scale)
         if event.type == gtk.gdk.BUTTON_RELEASE:
             if event.button == 1:
-                self.__sakura.notify_event('OnEBMouseClick', self.name,
-                                           x, y, self.id, 0)
+                self.__sakura.notify_event(
+                    'OnEBMouseClick', self.name, x, y, self.id, 0)
             elif event.button == 3:
-                self.__sakura.notify_event('OnEBMouseClick', self.name,
-                                           x, y, self.id, 1)
-        if self.clickerase():
+                self.__sakura.notify_event(
+                    'OnEBMouseClick', self.name, x, y, self.id, 1)
+        if self.clickerase:
             self.destroy()
         return True
 
     def motion_notify(self, widget, event):
+        scale = self.scale
         if self.x_root is not None and \
            self.y_root is not None:
             x_delta = int(event.x_root - self.x_root)
             y_delta = int(event.y_root - self.y_root)
             if event.state & gtk.gdk.BUTTON1_MASK:
-                if self.dragmove_horizontal():
-                    self.x += x_delta
-                if self.dragmove_vertical():
-                    self.y += y_delta
-                self.window.resize_move(int(self.x + self.action_x + self.vx),
-                                        int(self.y + self.action_y + self.vy))
+                if self.dragmove_horizontal:
+                    self.x += x_delta * 100 / scale
+                if self.dragmove_vertical:
+                    self.y += y_delta * 100 / scale
+                self.set_position()
                 self.x_root = event.x_root
                 self.y_root = event.y_root
         if self.move_notify_time is None or \
            time.time() - self.move_notify_time > 500 * 0.001:
-            x = int(event.x * 100 / self.scale)
-            y = int(event.y * 100 / self.scale)
-            self.__sakura.notify_event('OnEBMouseMove', self.name,
-                                       x, y, self.id)
+            x = int(event.x * 100 / scale)
+            y = int(event.y * 100 / scale)
+            self.__sakura.notify_event(
+                'OnEBMouseMove', self.name, x, y, self.id)
             self.move_notify_time = time.time()
         return True
 
index f632f13..e286337 100755 (executable)
@@ -174,6 +174,7 @@ class Installer:
                 target_dir = self.install_redo_supplement(filename, tmpdir, homedir)
             elif filetype == 'balloon':
                 target_dir = self.install_balloon(filename, tmpdir, homedir)
+                target_dir = None # XXX
             elif filetype == 'kinoko':
                 self.install_kinoko(filename, tmpdir, homedir)
                 target_dir = None # XXX
@@ -385,7 +386,6 @@ class Installer:
         # find balloon
         balloon_dir = inst and inst.get('balloon.directory')
         if balloon_dir:
-            balloon_list = []
             balloon_dir = ninix.home.get_normalized_path(balloon_dir)
             balloon_dst = os.path.join(homedir, 'balloon', balloon_dir)
             balloon_src = inst and inst.get('balloon.source.directory')
@@ -400,6 +400,7 @@ class Installer:
                 if os.path.exists(balloon_dst):
                     # uninstall older versions of the balloon
                     self.remove_files_and_dirs(balloon_dst, [])
+                balloon_list = []
                 for path in self.list_all_files(tmpdir, balloon_src):
                     balloon_list.append((os.path.join(tmpdir, path),
                                      os.path.join(homedir, 'balloon', path)))
@@ -475,7 +476,6 @@ class Installer:
                 return target
 
     def install_balloon(self, archive, srcdir, homedir):
-        filelist = []
         # find install.txt
         inst = ninix.home.read_install_txt(srcdir)
         if inst is None:
@@ -485,6 +485,7 @@ class Installer:
             fatal('"directory" not found in install.txt')
         target_dir = target_dir.encode('utf-8')
         dstdir = os.path.join(homedir, 'balloon', target_dir)
+        filelist = []
         for path in self.list_all_files(srcdir, ''):
             filelist.append((os.path.join(srcdir, path),
                              os.path.join(dstdir, path)))
@@ -527,7 +528,6 @@ class Installer:
                     shutil.rmtree(kinoko_dir)
 
     def install_kinoko(self, archive, srcdir, homedir):
-        filelist = []
         # find kinoko.ini
         kinoko = ninix.home.read_kinoko_ini(srcdir)
         if kinoko is None:
@@ -541,6 +541,7 @@ class Installer:
             dstdir = os.path.join(
                 homedir, 'kinoko', os.path.basename(archive)[:-4])
         # find files
+        filelist = []
         for filename in os.listdir(srcdir):
             path = os.path.join(srcdir, filename)
             if os.path.isfile(path):
@@ -559,7 +560,6 @@ class Installer:
             shutil.rmtree(nekoninni_dir)
 
     def install_nekoninni(self, archive, srcdir, homedir):
-        filelist = []
         # find install.txt
         inst = ninix.home.read_install_txt(srcdir)
         if inst is None:
@@ -570,6 +570,7 @@ class Installer:
         target_dir = target_dir.encode('utf-8')
         dstdir = os.path.join(homedir, 'nekodorif', 'skin', target_dir)
         # find files
+        filelist = []
         for filename in os.listdir(srcdir):
             path = os.path.join(srcdir, filename)
             if os.path.isfile(path):
@@ -589,7 +590,6 @@ class Installer:
             shutil.rmtree(katochan_dir)
 
     def install_katochan(self, archive, srcdir, homedir):
-        filelist = []
         # find install.txt
         inst = ninix.home.read_install_txt(srcdir)
         if inst is None:
@@ -600,6 +600,7 @@ class Installer:
         target_dir = target_dir.encode('utf-8')
         dstdir = os.path.join(homedir, 'nekodorif', 'katochan', target_dir)
         # find files
+        filelist = []
         for filename in os.listdir(srcdir):
             path = os.path.join(srcdir, filename)
             if os.path.isfile(path):
index 2433372..b115d6c 100644 (file)
@@ -54,9 +54,7 @@ class Menu:
             }
         self.__skin_list = None
         actions = gtk.ActionGroup('Actions')
-        entry = []
-        for value in self.__menu_list.itervalues():
-            entry.append(value[0])
+        entry = [value[0] for value in self.__menu_list.itervalues()]
         actions.add_actions(tuple(entry))
         ui_manager = gtk.UIManager()
         ui_manager.insert_action_group(actions, 0)
@@ -105,11 +103,11 @@ class Kinoko:
 
     def finalize(self):
         self.__running = 0
-        self.target.delete_observer(self)
+        self.target.detach_observer(self)
         if self.skin is not None:
             self.skin.destroy()
 
-    def notify_observer(self, event, args): ## FIXME
+    def observer_update(self, event, args):
         if self.skin is None:
             return
         if event in ['set position', 'set surface']:
@@ -119,7 +117,8 @@ class Kinoko:
             scale = self.target.get_surface_scale()
             self.skin.set_scale(scale)
         elif event == 'hide':
-            if not self.target.surface_is_shown(0):
+            side = args
+            if side == 0: # sakura side
                 self.skin.hide()
         elif event == 'iconified':
             self.skin.hide()
@@ -129,11 +128,11 @@ class Kinoko:
             self.finalize()
         elif event == 'move surface':
             side, xoffset, yoffset = args
-            if side == 0:
+            if side == 0: # sakura side
                 self.skin.set_position(xoffset, yoffset)
         elif event == 'raise':
             side = args
-            if side == 0:
+            if side == 0: # sakura side
                 self.skin.set_position() ## FIXME
         else:
             ##logging.debug('OBSERVER(kinoko): ignore - %s' % event)
@@ -154,7 +153,7 @@ class Kinoko:
     def load(self, data, target):
         self.data = data
         self.target = target
-        self.target.set_observer(self)
+        self.target.attach_observer(self)
         self.accelgroup = gtk.AccelGroup()
         self.load_skin()
         if self.skin is None:
@@ -212,7 +211,7 @@ class Skin:
             menu_callback[key] = self.callback[key]
         self.__menu = Menu(menu_callback, self.accelgroup)
         self.__scale = scale
-        self.__shown = 0
+        self.__shown = False
         self.surface_id = 0 # dummy
         self.window = ninix.pix.TransparentWindow()
         self.window.set_focus_on_map(False)
@@ -226,16 +225,16 @@ class Skin:
         self.window.add_accel_group(self.accelgroup) ## FIXME
         if self.data['animation'] is not None:
             path = os.path.join(self.data['dir'], self.data['animation'])
-            self.seriko = ninix.seriko.Controler(
-                {'': ninix.seriko.get_actors(ninix.config.create_from_file(path))})
+            actors = {'': ninix.seriko.get_actors(ninix.config.create_from_file(path))}
         else:
             base, ext = os.path.splitext(self.data['base'])
             path = os.path.join(self.data['dir'], ''.join((base, 'a.txt')))
             if os.path.exists(path):
-                self.seriko = ninix.seriko.Controler(
-                    {'': ninix.seriko.get_actors(ninix.config.create_from_file(path))})
+                actors = {'': ninix.seriko.get_actors(ninix.config.create_from_file(path))}
             else:
-                self.seriko = ninix.seriko.Controler({'': []})
+                actors = {'': []}
+        self.seriko = ninix.seriko.Controler(
+            actors, {'get_preference': self.get_preference})
         path = os.path.join(self.data['dir'], self.data['base'])
         try:
             self.pixbuf = ninix.pix.create_pixbuf_from_file(path)
@@ -277,15 +276,21 @@ class Skin:
         self.seriko.start(self)
         self.seriko.invoke_kinoko(self)
 
+    def get_preference(self, name): # dummy
+        if name == 'animation_quality':
+            return 1.0
+        else:
+            return None
+
     def show(self):
         if not self.__shown:
             self.window.show()
-            self.__shown = 1
+            self.__shown = True
 
     def hide(self):
         if self.__shown:
             self.window.hide()
-            self.__shown = 0
+            self.__shown = False
 
     def append_actor(self, frame, actor):
         self.seriko.append_actor(frame, actor)
index c30be53..a085d5c 100644 (file)
@@ -147,10 +147,8 @@ class Menu:
         self.__fontcolor = {'normal': '#000000', 'prelight': '#ffffff'} ## FIXME
         self.__sidebar_width = 0
         actions = gtk.ActionGroup('Actions')
-        entry = []
-        for key, value in self.__menu_list.iteritems():
-            if key != 'Stick':
-                entry.append(value[0])
+        entry = [value[0] for key, value in self.__menu_list.iteritems() \
+                     if key != 'Stick']
         actions.add_actions(tuple(entry))
         actions.add_toggle_actions(tuple([self.__menu_list['Stick'][0]]))
         self.ui_manager = gtk.UIManager()
index f4f3cd0..631073d 100644 (file)
@@ -85,9 +85,7 @@ class Menu:
             }
         self.__katochan_list = None
         actions = gtk.ActionGroup('Actions')
-        entry = []
-        for value in self.__menu_list.itervalues():
-            entry.append(value[0])
+        entry = [value[0] for value in self.__menu_list.itervalues()]
         actions.add_actions(tuple(entry))
         ui_manager = gtk.UIManager()
         ui_manager.insert_action_group(actions, 0)
@@ -128,7 +126,7 @@ class Nekoninni:
         self.skin = None
         self.katochan = None
 
-    def notify_observer(self, event, args): ## FIXME
+    def observer_update(self, event, args):
         if event in ['set position', 'set surface']:
             if self.skin is not None:
                 self.skin.set_position()
@@ -151,7 +149,7 @@ class Nekoninni:
             return 0
         self.dir = dir
         self.target = target
-        self.target.set_observer(self)
+        self.target.attach_observer(self)
         self.accelgroup = gtk.AccelGroup()
         scale = self.target.get_surface_scale()
         skin_callback ={
@@ -228,7 +226,7 @@ class Nekoninni:
 
     def finalize(self):
         self.__running = 0
-        self.target.delete_observer(self)
+        self.target.detach_observer(self)
         if self.katochan is not None:
             self.katochan.destroy()
         if self.skin is not None:
index 6bb8876..a2eb8ef 100644 (file)
@@ -26,8 +26,8 @@ class TransparentWindow(gtk.Window):
         gtk.Window.__init__(self, type)
         self.set_app_paintable(True)
         self.screen_changed()
-        self.truncated = False ## FIXME: property(dependent for truncate)
-        self.truncate = (0, 0) ## FIXME: property
+        self.truncated = False
+        self.mask_offset = (0, 0)
         fixed = gtk.Fixed()
         fixed.show()
         gtk.Window.add(self, fixed) # XXX
@@ -38,7 +38,7 @@ class TransparentWindow(gtk.Window):
         self.connect_after('size_allocate', self.size_allocate)
 
     def set_mask(self, mask):
-        x, y = self.truncate
+        x, y = self.mask_offset
         self.__mask = mask
         if self.is_composited():
             self.input_shape_combine_mask(mask, x, y)
@@ -89,7 +89,7 @@ class TransparentWindow(gtk.Window):
             self.input_shape_combine_mask(self.mask, 0, 0)
         else:
             self.shape_combine_mask(self.mask, 0, 0)
-        self.truncate = (0, 0)
+        self.mask_offset = (0, 0)
         self.truncated = False
 
     def resize_move(self, x, y, xoffset=0, yoffset=0):
@@ -135,7 +135,7 @@ class TransparentWindow(gtk.Window):
             else:
                 self.shape_combine_mask(self.mask, offset_x, offset_y)
             self.truncated = True
-            self.truncate = (offset_x, offset_y)
+            self.mask_offset = (offset_x, offset_y)
         self.__position = (new_x, new_y)
         self.__child.queue_draw()
 
index 81071ec..eb6e708 100644 (file)
@@ -80,7 +80,7 @@ class Sakura:
         self.sstp_handle = None
         self.sstp_entry_db = None
         self.sstp_request_handler = None
-        # error: 'loose'(default) or 'strict'
+        # error = 'loose'(default) or 'strict'
         self.script_parser = ninix.script.Parser(error='loose')
         self.char = 2 # 'sakura' and 'kero'
         self.script_queue = []
@@ -115,7 +115,7 @@ class Sakura:
         self.__dll = ninix.dll.Library(
             'shiori', default_path, saori_lib=saori_lib)
         self.__temp_mode = 0
-        self.__observer = []
+        self.__observers = {}
         balloon_callback = {
             'is_paused': self.is_paused,
             'notify_balloon_click': self.notify_balloon_click,
@@ -193,19 +193,18 @@ class Sakura:
     def select_kinoko(self, event, item):
         self.callback['select_kinoko'](item, self)
 
-    def set_observer(self, observer):
-        if observer not in self.__observer:
-            self.__observer.append(observer)
+    def attach_observer(self, observer):
+        if observer not in self.__observers:
+            self.__observers[observer] = 1
 
-    def notify_observer(self, event, args=()):
-        for observer in self.__observer:
-            observer.notify_observer(event, args)
+    def notify_observer(self, event, args=None):
+        args = args or ()
+        for observer in self.__observers.keys():
+            observer.observer_update(event, args)
 
-    def delete_observer(self, observer):
-        for i in range(len(self.__observer)):
-            if self.__observer[i] == observer:
-                del self.__observer[i]
-                break
+    def detach_observer(self, observer):
+        if observer in self.__observers:
+            del self.__observers[observer]
 
     def new(self, desc, shiori_dir, use_makoto, surface_set, prefix,
             shiori_dll, shiori_name): ## FIXME
@@ -447,40 +446,10 @@ class Sakura:
         self.idle_start = time.time()
 
     def notify_preference_changed(self): ## FIXME
-        self.balloon.reset_fonts()
-        flag = self.callback['get_preference']('use_pna')
-        self.set_use_pna(flag)
-        scale = self.callback['get_preference']('surface_scale')
-        self.set_surface_scale(scale)
-        flag = self.callback['get_preference']('balloon_scalling')
-        self.set_balloon_scalling(flag)
-        alpha = self.callback['get_preference']('surface_alpha')
-        self.set_surface_alpha(alpha)
-        alpha = self.callback['get_preference']('balloon_alpha')
-        self.set_balloon_alpha(alpha)
-        quality = self.callback['get_preference']('animation_quality')
-        self.set_animation_quality(quality)
-        flag = self.callback['get_preference']('seriko_inactive')
-        self.set_seriko_inactive(flag)
-
-    def set_use_pna(self, flag): ## FIXME
-        self.surface.set_use_pna(flag)
-        self.balloon.set_use_pna(flag)
-
-    def set_surface_alpha(self, alpha): ## FIXME
-        self.surface.set_alpha_channel(alpha)
-
-    def set_balloon_alpha(self, alpha): ## FIXME
-        self.balloon.set_alpha_channel(alpha)
-
-    def set_animation_quality(self, quality): ## FIXME
-        self.surface.set_animation_quality(quality)
-
-    def set_seriko_inactive(self, flag): ## FIXME
-        self.surface.set_seriko_inactive(flag)
-
-    def set_balloon_scalling(self, flag): ## FIXME
-        self.balloon.set_scalling(flag)
+        self.balloon.reset_fonts() ## FIXME
+        self.surface.reset_surface()
+        self.notify_observer('set scale') ## FIXME
+        self.balloon.reset_balloon()
 
     def get_surface_position(self, side):
         result = self.surface.get_position(side)
@@ -695,6 +664,7 @@ class Sakura:
             self.reset_script(1)
             self.stand_by(1)
             self.notify_event('OnWindowStateMinimize')
+        self.notify_observer('iconified')
 
     def notify_deiconified(self):
         if not self.cantalk:
@@ -702,6 +672,7 @@ class Sakura:
             self.callback['select_current_sakura']()
             if not self.passivemode:
                 self.notify_event('OnWindowStateRestore')
+        self.notify_observer('deiconified')
 
     def notify_link_selection(self, link_id, text, number):
         if self.script_origin == self.FROM_SSTP_CLIENT and \
@@ -956,11 +927,7 @@ class Sakura:
         self.surface.set_surface_default(side)
 
     def get_surface_scale(self):
-        return self.surface.get_scale()
-
-    def set_surface_scale(self, scale):
-        self.surface.set_scale(scale)
-        self.balloon.set_scale(scale)
+        return self.callback['get_preference']('surface_scale')
 
     def get_surface_size(self, side):
         result = self.surface.get_surface_size(side)
index 429e8b0..b065451 100644 (file)
@@ -89,6 +89,7 @@ class ParserError(Exception):
             dump = self.src
         return 'ParserError: column %s: %s\n%s' % (column, self.message, dump)
 
+
 class Parser:
 
     def __init__(self, error='strict'):
@@ -142,7 +143,8 @@ class Parser:
         return token, lexeme
 
     def parse(self, s):
-        if not s: return []
+        if not s:
+            return []
         # tokenize the script
         self.src = s
         self.tokens = self.tokenize(self.src)
index ca37a22..3ade5f0 100644 (file)
@@ -24,8 +24,9 @@ class Controler:
 
     DEFAULT_FPS = 30.0 # current default
 
-    def __init__(self, seriko):
+    def __init__(self, seriko, callback):
         self.seriko = seriko
+        self.callback = callback
         self.exclusive_actor = None
         self.base_id = None
         self.timeout_id = None
@@ -37,10 +38,6 @@ class Controler:
         self.active = []
         self.__move = None
         self.__dirty = 1
-        self.__inactive = False
-
-    def is_active(self):
-        return not bool(self.__inactive)
 
     def set_base_id(self, window, surface_id):
         if surface_id == '-2':
@@ -58,21 +55,6 @@ class Controler:
     def append_actor(self, frame, actor):
         self.active.append((frame, actor))
 
-    def set_animation_quality(self, quality, window):
-        if self.timeout_id is not None:
-            glib.source_remove(self.timeout_id)
-        if quality is None:
-            quality = 1.0
-        self.fps = self.DEFAULT_FPS * quality
-        if self.timeout_id is not None:
-            self.timeout_id = glib.timeout_add(
-                int(1000.0 / self.fps), # [msec]
-                self.update, window)
-
-    def set_inactive(self, flag, window):
-        self.__inactive = bool(flag)
-        self.__dirty = 1 # XXX
-
     def update_frame(self, window):
         frame, actor = self.get_actor_next(window)
         last_actor = actor
@@ -94,6 +76,8 @@ class Controler:
     def update(self, window):
         ## FIXME: Use g_source_get_time. (glib.glib_version > (2, 27, 3))
         current_tick = glib.get_current_time() # [sec]
+        quality = self.callback['get_preference']('animation_quality')
+        self.fps = self.DEFAULT_FPS * quality
         if self.prev_tick == 0: ## First time
             delta_tick = 1000.0 / self.fps # [msec]
         else:
@@ -107,7 +91,9 @@ class Controler:
         if self.__move is not None:
             window.move_surface(*self.__move)
             self.__move = None
-        return True
+        self.timeout_id = glib.timeout_add(int(1000.0 / self.fps), # [msec]
+                                           self.update, window)
+        return False
 
     def lock_exclusive(self, window, actor):
         assert self.exclusive_actor is None
index 6502e76..0087e6f 100644 (file)
@@ -36,49 +36,11 @@ class Surface:
     def __init__(self, callback):
         self.window = []
         self.__menu = None
-        self.__scale = 100 # %
         self.desc = None
-        self.__use_pna = False
-        self.__alpha_channel = 1.0
         self.callback = callback
         self.mikire = 0
         self.kasanari = 0
 
-    def set_use_pna(self, flag):
-        if self.__use_pna == bool(flag):
-            return
-        self.__use_pna = bool(flag)
-        for surface_window in self.window:
-            surface_window.set_use_pna(self.__use_pna)
-
-    def set_alpha_channel(self, alpha):
-        if not 0.1 <= alpha <= 1.0 or alpha is None:
-            alpha = 1.0
-        if self.__alpha_channel == alpha:
-            return
-        self.__alpha_channel = alpha
-        for surface_window in self.window:
-            surface_window.set_alpha_channel(self.__alpha_channel)
-
-    def get_scale(self):
-        return self.__scale
-
-    def set_scale(self, scale):
-        if self.__scale == scale:
-            return
-        self.__scale = scale # %
-        for surface_window in self.window:
-            surface_window.set_scale(self.__scale)
-        self.callback['notify_observer']('set scale') ## FIXME
-
-    def set_animation_quality(self, quality):
-        for surface_window in self.window:
-            surface_window.set_animation_quality(quality)
-
-    def set_seriko_inactive(self, flag):
-        for surface_window in self.window:
-            surface_window.set_seriko_inactive(flag)
-
     def finalize(self):
         for surface_window in self.window:
             surface_window.destroy()
@@ -114,21 +76,21 @@ class Surface:
                 
     def window_iconify(self, flag):
         gtk_window = self.window[0].window
-        if flag:
-            if not gtk_window.window.get_state() & \
-               gtk.gdk.WINDOW_STATE_ICONIFIED:
-                gtk_window.iconify()
-        else:
-            if gtk_window.window.get_state() & gtk.gdk.WINDOW_STATE_ICONIFIED:
-                gtk_window.deiconify()
+        iconified = gtk_window.window.get_state() & \
+            gtk.gdk.WINDOW_STATE_ICONIFIED
+        if flag and not iconified:
+            gtk_window.iconify()
+        elif not flag and iconified:
+            gtk_window.deiconify()
 
     def window_state(self, window, event):
         if not self.callback['is_running']():
             return
+        if not (event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED):
+            return
         if event.new_window_state & gtk.gdk.WINDOW_STATE_ICONIFIED:
             if window == self.window[0].window:
                 self.callback['notify_iconified']()
-                self.callback['notify_observer']('iconified')
             for surface_window in self.window:
                 gtk_window = surface_window.window
                 if gtk_window != window and \
@@ -414,16 +376,6 @@ class Surface:
             self.__surface, tooltips, self.__pixbufs, seriko, self.__region,
             mayuna, bind, default)
         self.window.append(surface_window)
-        # SurfaceWindow default settings should be same as current Surface ones
-        surface_window.set_scale(self.__scale)
-        surface_window.set_use_pna(self.__use_pna)
-        surface_window.set_alpha_channel(self.__alpha_channel)
-        # Seriko default settings
-        quality = self.callback['get_preference']('animation_quality')
-        surface_window.set_animation_quality(quality)
-        flag = self.callback['get_preference']('seriko_inactive')
-        surface_window.set_seriko_inactive(flag)
-        #surface_window.set_surface(default) # Don't do this.
 
     def get_mayuna_menu(self):
         for side, index in [('sakura', 0), ('kero', 1)]:
@@ -479,9 +431,9 @@ class Surface:
         else: 
             return None
 
-    def reset_surface(self, side):
-        if len(self.window) > side:
-            self.window[side].reset_surface()
+    def reset_surface(self):
+        for window in self.window:
+            window.reset_surface()
 
     def set_surface_default(self, side):
         if side is None:
@@ -530,16 +482,10 @@ class Surface:
         else:
             return None, None
 
-    def get_direction(self, side):
-        if len(self.window) > side:
-            return self.window[side].get_direction()
-        else:
-            return 0
-
     def reset_balloon_position(self):
         for side in range(len(self.window)):
             x, y = self.get_position(side)
-            direction = self.get_direction(side)
+            direction = self.window[side].direction
             ox, oy = self.get_balloon_offset(side)
             self.callback['set_balloon_direction'](side, direction)
             if direction == 0: # left
@@ -616,38 +562,30 @@ class Surface:
     def show(self, side):
         if len(self.window) > side:
             self.window[side].show()
-            self.callback['notify_observer']('show') ## FIXME
-            self.callback['notify_observer']('raise', (side)) # XXX ## FIXME
 
     def hide_all(self):
         for side in range(len(self.window)):
             self.window[side].hide()
-        self.callback['notify_observer']('hide') ## FIXME
 
     def hide(self, side):
         if len(self.window) > side:
             self.window[side].hide()
-        self.callback['notify_observer']('hide') ## FIXME
 
     def raise_all(self):
         for side in range(len(self.window)):
             self.window[side].raise_()
-            self.callback['notify_observer']('raise', (side)) ## FIXME
 
     def raise_(self, side):
         if len(self.window) > side:
             self.window[side].raise_()
-            self.callback['notify_observer']('raise', (side)) ## FIXME
 
     def lower_all(self):
         for side in range(len(self.window)):
             self.window[side].lower()
-        self.callback['notify_observer']('lower') ## FIXME
 
     def lower(self, side):
         if len(self.window) > side:
             self.window[side].lower()
-        self.callback['notify_observer']('lower') ## FIXME
 
     def invoke(self, side, actor_id):
         if len(self.window) > side:
@@ -735,7 +673,7 @@ class Surface:
             self.kasanari = 0
 
 
-class SurfaceWindow:
+class SurfaceWindow(object):
 
     # DnD data types
     dnd_targets = [
@@ -751,17 +689,17 @@ class SurfaceWindow:
         self.alias = alias
         self.tooltips = tooltips
         self.align = 0
-        self.__scale = 100 # %
-        self.__use_pna = False
-        self.__alpha_channel = 1.0
-        self.__current_part = ''
+        self.__current_part = '' ## FIXME
         if self.alias is not None:
             default_id = self.alias.get(default_id, [default_id])[0]
         self.surface = surface
         self.surface_id = default_id
         self.pixbuf = pixbuf
         self.current_surface_pixbuf = None # XXX
-        self.seriko = ninix.seriko.Controler(seriko)
+        seriko_callback = {
+            'get_preference': self.callback['get_preference'],
+            }
+        self.seriko = ninix.seriko.Controler(seriko, seriko_callback)
         self.region = region
         self.mayuna = mayuna
         self.bind = bind
@@ -769,7 +707,7 @@ class SurfaceWindow:
         self.__shown = False
         self.window_offset = (0, 0)
         self.set_position(0, 0)
-        self.set_direction(0)
+        self.__direction = 0
         self.dragged = False
         self.x_root = None
         self.y_root = None
@@ -797,26 +735,18 @@ class SurfaceWindow:
         self.window.window.set_back_pixmap(None, False)
         self.set_surface(None)
 
-    def set_use_pna(self, flag):
-        self.__use_pna = bool(flag)
-        self.reset_surface()
-
-    def set_alpha_channel(self, value):
-        self.__alpha_channel = value
-        self.reset_surface()
+    def get_direction(self):
+        return self.__direction
 
-    def get_scale(self):
-        return self.__scale
-
-    def set_scale(self, scale):
-        self.__scale = scale # %
-        self.reset_surface()
+    def set_direction(self, direction):
+        self.__direction = direction # 0: left, 1: right
+        self.callback['set_balloon_direction'](self.side, direction)
 
-    def set_animation_quality(self, quality):
-        self.seriko.set_animation_quality(quality, self)
+    direction = property(get_direction, set_direction)
 
-    def set_seriko_inactive(self, flag):
-        self.seriko.set_inactive(flag, self)
+    @property
+    def scale(self):
+        return self.callback['get_preference']('surface_scale')
 
     def drag_data_received(self, widget, context, x, y, data, info, time):
         logging.debug('Content-type: %s' % data.type)
@@ -942,16 +872,17 @@ class SurfaceWindow:
 
     def create_pixbuf_from_file(self, pixbuf_id):
         assert pixbuf_id in self.pixbuf
+        use_pna = self.callback['get_preference']('use_pna')
         try:
             pixbuf = ninix.pix.create_pixbuf_from_file(
-                self.pixbuf[pixbuf_id][0], use_pna=self.__use_pna)
+                self.pixbuf[pixbuf_id][0], use_pna=use_pna)
         except:
             logging.debug('cannot load surface #%s' % pixbuf_id)
             return ninix.pix.create_blank_pixbuf(100, 100)
         for element, x, y in self.pixbuf[pixbuf_id][1:]:
             try:
                 overlay = ninix.pix.create_pixbuf_from_file(
-                    element, use_pna=self.__use_pna)
+                    element, use_pna=use_pna)
             except:
                 continue
             w = overlay.get_width()
@@ -986,7 +917,7 @@ class SurfaceWindow:
     def draw_region(self):
         cr = self.darea.window.cairo_create()
         cr.save()
-        scale = self.get_scale()
+        scale = self.scale
         for part, x1, y1, x2, y2 in self.collisions:
             x1 = x1 * scale / 100
             x2 = x2 * scale / 100
@@ -1042,7 +973,7 @@ class SurfaceWindow:
         return surface_pixbuf
 
     def update_frame_buffer(self):
-        if self.seriko.is_active():
+        if not self.callback['get_preference']('seriko_inactive'):
             surface_pixbuf = self.create_surface_pixbuf(self.seriko.base_id)
             assert surface_pixbuf is not None
             # draw overlays
@@ -1077,7 +1008,7 @@ class SurfaceWindow:
             surface_pixbuf = self.create_surface_pixbuf()
         w = surface_pixbuf.get_width()
         h = surface_pixbuf.get_height()
-        scale = self.get_scale()
+        scale = self.scale
         w = max(8, w * scale / 100)
         h = max(8, h * scale / 100)
         surface_pixbuf = surface_pixbuf.scale_simple(
@@ -1098,7 +1029,8 @@ class SurfaceWindow:
         cr.paint()
         cr.restore()
         cr.set_source_pixbuf(self.current_surface_pixbuf, 0, 0)
-        cr.paint_with_alpha(self.__alpha_channel)
+        alpha_channel = self.callback['get_preference']('surface_alpha')
+        cr.paint_with_alpha(alpha_channel)
         del cr
         if self.callback['get_preference']('check_collision'):
             self.draw_region()
@@ -1135,7 +1067,7 @@ class SurfaceWindow:
             x = self.desc.get_with_type('%s.balloon.offsetx' % name, int, 0)
         if y is None:
             y = self.desc.get_with_type('%s.balloon.offsety' % name, int, 0)
-        scale = self.get_scale()
+        scale = self.scale
         x = x * scale / 100
         y = y * scale / 100
         return x, y
@@ -1143,7 +1075,7 @@ class SurfaceWindow:
     def get_collision_area(self, part):
         for p, x1, y1, x2, y2 in self.collisions: ## FIXME
             if p == part:
-                scale = self.get_scale()
+                scale = self.scale
                 x1 = x1 * scale / 100
                 x2 = x2 * scale / 100
                 y1 = y1 * scale / 100
@@ -1161,7 +1093,7 @@ class SurfaceWindow:
             w, h = 100, 100 # XXX
         else:
             w, h = ninix.pix.get_png_size(self.pixbuf[surface_id][0])
-        scale = self.get_scale()
+        scale = self.scale
         w = max(8, int(w * scale / 100))
         h = max(8, int(h * scale / 100))
         return w, h
@@ -1181,7 +1113,7 @@ class SurfaceWindow:
         path, config = self.surface[basename]
         value = config.get_with_type(name, conv)
         if value is not None:
-            scale = self.get_scale()
+            scale = self.scale
             value = conv(value * scale / 100)
         return value
 
@@ -1195,24 +1127,14 @@ class SurfaceWindow:
         centery = self.__get_with_scaling('point.kinoko.centery', int)
         return centerx, centery
 
-    def get_direction(self): ## FIXME: property
-        return self.direction
-
-    def set_direction(self, direction): ## FIXME: property
-        self.direction = direction # 0: left, 1: right
-        self.callback['set_balloon_direction'](self.side, direction)
-
     def set_position(self, x, y):
         self.position = (x, y)
         new_x, new_y = self.get_position()
         if self.__shown:
             self.window.resize_move(new_x, new_y)
         left, top, scrn_w, scrn_h = ninix.pix.get_workarea()
-        if x > left + scrn_w / 2:
-            direction = 0
-        else:
-            direction = 1
-        self.set_direction(direction)
+        direction = 0 if x > left + scrn_w / 2 else 1
+        self.direction = direction
         ox, oy = self.get_balloon_offset()
         if direction == 0: # left
             base_x = new_x + ox
@@ -1270,23 +1192,28 @@ class SurfaceWindow:
         self.window.resize_move(x, y) # XXX: call before showing the window
         self.darea.show()
         self.window.show()
+        self.callback['notify_observer']('show', (self.side))
+        self.callback['notify_observer']('raise', (self.side))
 
     def hide(self):
         if self.__shown:
             self.window.hide()
             self.__shown = False
+            self.callback['notify_observer']('hide', (self.side))
 
     def raise_(self):
         self.window.window.raise_()
+        self.callback['notify_observer']('raise', (self.side))
 
     def lower(self):
         self.window.window.lower()
+        self.callback['notify_observer']('lower', (self.side))
 
     def button_press(self, window, event):
         self.callback['reset_idle_time']()
         x = int(event.x)
         y = int(event.y)
-        scale = self.get_scale()
+        scale = self.scale
         x = int(x * 100 / scale)
         y = int(y * 100 / scale)
         self.x_root = event.x_root
@@ -1311,7 +1238,7 @@ class SurfaceWindow:
             x, y, state = self.darea.window.get_pointer()
         else:
             x, y, state = event.x, event.y, event.state
-        scale = self.get_scale()
+        scale = self.scale
         x = int(x * 100 / scale)
         y = int(y * 100 / scale)
         part = self.get_touched_region(x, y)
@@ -1354,7 +1281,7 @@ class SurfaceWindow:
     def scroll(self, darea, event):
         x = int(event.x)
         y = int(event.y)
-        scale = self.get_scale()
+        scale = self.scale
         x = int(x * 100 / scale)
         y = int(y * 100 / scale)
         if event.direction == gtk.gdk.SCROLL_UP:
@@ -1377,7 +1304,7 @@ class SurfaceWindow:
 
     def window_enter_notify(self, window, event):
         x, y, state = event.x, event.y, event.state
-        scale = self.get_scale()
+        scale = self.scale
         x = int(x * 100 / scale)
         y = int(y * 100 / scale)
         self.callback['notify_event'](
@@ -1385,7 +1312,7 @@ class SurfaceWindow:
 
     def window_leave_notify(self, window, event):
         x, y, state = event.x, event.y, event.state
-        scale = self.get_scale()
+        scale = self.scale
         x = int(x * 100 / scale)
         y = int(y * 100 / scale)
         if self.__current_part != '': # XXX
index db471ba..d572d09 100644 (file)
@@ -10,7 +10,7 @@
 #  PURPOSE.  See the GNU General Public License for more details.
 #
 
-NUMBER = '4.2.5'
+NUMBER = '4.2.6'
 CODENAME = 'voodoo programming'
 
 VERSION = '%s (%s)' % (NUMBER, CODENAME)