OSDN Git Service

1.1.0
[pydun/Pydun.git] / Pydun.py
index 770870b..ad2399c 100644 (file)
--- a/Pydun.py
+++ b/Pydun.py
@@ -1,16 +1,19 @@
-#!/usr/bin/env python\r
+#!/usr/bin/env python\r
 # -*- coding: utf-8 -*-\r
 \r
 #Pydun.py - mapping tool\r
 #copyright (c) 2013 WATAHIKI Hiroyuki\r
-#url: http://sourceforge.jp/projects/pydun/\r
+#url: http://osdn.jp/projects/pydun/\r
 #email: hrwatahiki at gmail.com\r
+#twitter: @hrwatahiki\r
+#blog: http://hrwatahiki.blogspot.jp/\r
 \r
 \r
 import sys\r
 import os.path\r
-import codecs\r
 import locale\r
+import urllib.request, urllib.parse, urllib.error\r
+import xml.etree.ElementTree\r
 import webbrowser\r
 from PySide import QtCore, QtGui\r
 import yaml\r
@@ -20,8 +23,9 @@ _mapengine = None
 _mapimages = None\r
 _undomanager = None\r
 \r
-projecturl = "http://sourceforge.jp/projects/pydun/"\r
-projectversion = "1.0.3"\r
+projecturl = "http://osdn.jp/projects/pydun/"\r
+projectrssurl = "http://osdn.jp/projects/pydun/releases/rss"\r
+projectversion = "1.1.0"\r
 \r
 \r
 class MainWindow(QtGui.QMainWindow):\r
@@ -30,22 +34,23 @@ class MainWindow(QtGui.QMainWindow):
         global _mapengine\r
         global _mapimages\r
         global _undomanager\r
+        global config\r
         super(MainWindow, self).__init__(parent)\r
 \r
         _undomanager = UndoManager()\r
-        _mapimages = MapImages()\r
+        _mapimages = MapImages(config.get("showWallMenuString", False))\r
         self.setmenu()\r
         _undomanager.changed.connect(self.updateundostate)\r
 \r
         self.new()\r
         if len(sys.argv) >= 2:\r
-            self.open(unicode(sys.argv[1], locale.getpreferredencoding()))\r
+            self.open(sys.argv[1])\r
 \r
         self.mainframe = MainFrame(self)\r
         self.setCentralWidget(self.mainframe)\r
 \r
         self.statusbar = QtGui.QStatusBar(self)\r
-        self.statusbar.showMessage(u"")\r
+        self.statusbar.showMessage("")\r
         self.setStatusBar(self.statusbar)\r
         if "windowSize" in config:\r
             self.resize(\r
@@ -54,62 +59,68 @@ class MainWindow(QtGui.QMainWindow):
                     config["windowSize"]["height"]))\r
 \r
     def setmenu(self):\r
+        global config\r
         #File menu\r
-        filemenu = self.menuBar().addMenu(u"ファイル(&F)")\r
+        filemenu = self.menuBar().addMenu("ファイル(&F)")\r
 \r
-        newact = QtGui.QAction(u"新規(&N)", self)\r
+        newact = QtGui.QAction("新規(&N)", self)\r
         newact.triggered.connect(self.new_triggered)\r
         newact.setShortcut(QtGui.QKeySequence.New)\r
         filemenu.addAction(newact)\r
 \r
-        openact = QtGui.QAction(u"開く(&O)...", self)\r
+        openact = QtGui.QAction("開く(&O)...", self)\r
         openact.triggered.connect(self.open_triggered)\r
         openact.setShortcut(QtGui.QKeySequence.Open)\r
         filemenu.addAction(openact)\r
 \r
-        saveact = QtGui.QAction(u"上書き保存(&S)", self)\r
+        saveact = QtGui.QAction("上書き保存(&S)", self)\r
         saveact.triggered.connect(self.save_triggered)\r
         saveact.setShortcut(QtGui.QKeySequence.Save)\r
         filemenu.addAction(saveact)\r
 \r
-        saveasact = QtGui.QAction(u"名前をつけて保存(&A)...", self)\r
+        saveasact = QtGui.QAction("名前をつけて保存(&A)...", self)\r
         saveasact.triggered.connect(self.saveas_triggered)\r
         saveasact.setShortcut(QtGui.QKeySequence.SaveAs)\r
         filemenu.addAction(saveasact)\r
 \r
-        exitact = QtGui.QAction(u"終了(&E)", self)\r
+        exitact = QtGui.QAction("終了(&E)", self)\r
         exitact.triggered.connect(self.exit_triggered)\r
         exitact.setShortcut(QtGui.QKeySequence.Quit)\r
         filemenu.addAction(exitact)\r
 \r
         #Edit menu\r
-        editmenu = self.menuBar().addMenu(u"編集(&E)")\r
-        self.undoact = QtGui.QAction(u"元に戻す(&U)", self)\r
+        editmenu = self.menuBar().addMenu("編集(&E)")\r
+        self.undoact = QtGui.QAction("元に戻す(&U)", self)\r
         self.undoact.triggered.connect(self.undo_triggered)\r
         self.undoact.setShortcut(QtGui.QKeySequence.Undo)\r
         editmenu.addAction(self.undoact)\r
-        self.redoact = QtGui.QAction(u"やり直し(&R)", self)\r
+        self.redoact = QtGui.QAction("やり直し(&R)", self)\r
         self.redoact.triggered.connect(self.redo_triggered)\r
         self.redoact.setShortcut(QtGui.QKeySequence.Redo)\r
         editmenu.addAction(self.redoact)\r
         editmenu.addSeparator()\r
-        setmapsizeact = QtGui.QAction(u"マップのサイズ(&S)", self)\r
+        setmapsizeact = QtGui.QAction("マップのサイズ(&S)", self)\r
         setmapsizeact.triggered.connect(self.setmapsize_triggered)\r
         editmenu.addAction(setmapsizeact)\r
-        setorigineact = QtGui.QAction(u"座標設定(&O)", self)\r
+        setorigineact = QtGui.QAction("座標設定(&O)", self)\r
         setorigineact.triggered.connect(self.setorigine_triggered)\r
         editmenu.addAction(setorigineact)\r
+        wallmenustringact = QtGui.QAction("壁メニューに文字を表示する(&W)", self)\r
+        wallmenustringact.setCheckable(True)\r
+        wallmenustringact.setChecked(config.get("showWallMenuString", False))\r
+        wallmenustringact.triggered.connect(self.togglewallmenustring_triggered)\r
+        editmenu.addAction(wallmenustringact)\r
 \r
         #Help menu\r
-        helpmenu = self.menuBar().addMenu(u"ヘルプ(&H)")\r
-        tutorialact = QtGui.QAction(u"ヘルプの表示(&H)", self)\r
+        helpmenu = self.menuBar().addMenu("ヘルプ(&H)")\r
+        tutorialact = QtGui.QAction("ヘルプの表示(&H)", self)\r
         tutorialact.triggered.connect(self.tutorial_triggered)\r
         tutorialact.setShortcut(QtGui.QKeySequence.HelpContents)\r
         helpmenu.addAction(tutorialact)\r
-        projectact = QtGui.QAction(u"プロジェクトのWebサイト(&W)", self)\r
+        projectact = QtGui.QAction("プロジェクトのWebサイト(&W)", self)\r
         projectact.triggered.connect(self.project_triggered)\r
         helpmenu.addAction(projectact)\r
-        aboutact = QtGui.QAction(u"Pydunについて(&A)...", self)\r
+        aboutact = QtGui.QAction("Pydunについて(&A)...", self)\r
         aboutact.triggered.connect(self.about_triggered)\r
         helpmenu.addAction(aboutact)\r
 \r
@@ -125,48 +136,62 @@ class MainWindow(QtGui.QMainWindow):
             self.redoact.setDisabled(True)\r
 \r
     def setTitle(self, filename):\r
+        s = self.getfilename(filename) + " - Pydun"\r
+        self.setWindowTitle(s)\r
+\r
+    def getfilename(self, filename):\r
         if filename == None:\r
-            s = u"新規作成"\r
+            s = "無題"\r
         else:\r
-            s = os.path.basename(filename)\r
-        s ="Pydun - " + s\r
-        self.setWindowTitle(s)\r
+            s = os.path.splitext(os.path.basename(filename))[0]\r
+        return s\r
 \r
     @QtCore.Slot()\r
     def new_triggered(self):\r
-        if QtGui.QMessageBox.Ok == QtGui.QMessageBox.question(\r
-            self, u"確認", u"新しいマップを作成しますか?",\r
-            (QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)):\r
+        if self.confirmdiscarding():\r
             self.new()\r
+            return True\r
+        return False\r
 \r
     def new(self):\r
         global _mapengine\r
         _mapengine = MapEngine(20, 20, 1, -1, 0, +19)\r
-        _undomanager.clear()\r
-        _undomanager.save(_mapengine.savestring())\r
+        _undomanager.init(_mapengine.savestring())\r
         self.setTitle(None)\r
         try:\r
             self.mainframe.mapframe.repaint()\r
         except:\r
             pass\r
 \r
+    def confirmdiscarding(self):\r
+        if not _undomanager.commited:\r
+            dlg = PydunAskSaveDialog(self, self.getfilename(_mapengine.filename))\r
+            ret = dlg.exec_()\r
+            if ret == QtGui.QMessageBox.Cancel:\r
+                return False\r
+            elif ret == QtGui.QMessageBox.Save:\r
+                saved = self.save_triggered()\r
+                if not saved:\r
+                    return False\r
+        return True\r
+\r
     @QtCore.Slot()\r
     def open_triggered(self):\r
-        d = ""\r
-        try:\r
-            d = os.path.dirname(_mapengine.filename)\r
-        except:\r
-            pass\r
-        filename = QtGui.QFileDialog.getOpenFileName(\r
-            dir=d,\r
-            filter=u"*.pydun;;*.*", selectedFilter=u"*.pydun")\r
-        if filename[0] != u"":\r
-            self.open(filename[0])\r
+        if self.confirmdiscarding():\r
+            d = ""\r
+            try:\r
+                d = os.path.dirname(_mapengine.filename)\r
+            except:\r
+                pass\r
+            filename = QtGui.QFileDialog.getOpenFileName(\r
+                dir=d,\r
+                filter="*.pydun;;*.*", selectedFilter="*.pydun")\r
+            if filename[0] != "":\r
+                self.open(filename[0])\r
 \r
     def open(self, filename):\r
         _mapengine.load(filename)\r
-        _undomanager.clear()\r
-        _undomanager.save(_mapengine.savestring())\r
+        _undomanager.init(_mapengine.savestring())\r
         self.setTitle(_mapengine.filename)\r
         try:\r
             self.mainframe.mapframe.repaint()\r
@@ -177,8 +202,10 @@ class MainWindow(QtGui.QMainWindow):
     def save_triggered(self):\r
         if _mapengine.filename:\r
             self.save(_mapengine.filename)\r
+            saved = True\r
         else:\r
-            self.saveas_triggered()\r
+            saved = self.saveas_triggered()\r
+        return saved\r
 \r
     @QtCore.Slot()\r
     def saveas_triggered(self):\r
@@ -189,12 +216,16 @@ class MainWindow(QtGui.QMainWindow):
             pass\r
         filename = QtGui.QFileDialog.getSaveFileName(\r
             dir=d,\r
-            filter=u"*.pydun;;*.*", selectedFilter=u"*.pydun")\r
-        if filename[0] != u"":\r
+            filter="*.pydun;;*.*", selectedFilter="*.pydun")\r
+        if filename[0] != "":\r
             self.save(filename[0])\r
+            return True\r
+        else:\r
+            return False\r
 \r
     def save(self, filename):\r
         _mapengine.save(filename)\r
+        _undomanager.commit()\r
         self.setTitle(_mapengine.filename)\r
 \r
     @QtCore.Slot()\r
@@ -210,9 +241,7 @@ class MainWindow(QtGui.QMainWindow):
     def exit(self):\r
         global config\r
         global configfilename\r
-        if QtGui.QMessageBox.Ok == QtGui.QMessageBox.question(\r
-            self, u"確認", u"終了しますか?",\r
-            (QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel)):\r
+        if self.confirmdiscarding():\r
             config["windowSize"] = dict()\r
             config["windowSize"]["width"] = self.size().width()\r
             config["windowSize"]["height"] = self.size().height()\r
@@ -236,14 +265,14 @@ class MainWindow(QtGui.QMainWindow):
 \r
     @QtCore.Slot()\r
     def setorigine_triggered(self):\r
-        title = u"座標設定"\r
+        title = "座標設定"\r
         if self.mainframe.mapframe.setoriginemode:\r
             QtGui.QMessageBox.information(\r
-                self, title, u"座標設定を中止します。", QtGui.QMessageBox.Ok)\r
+                self, title, "座標設定を中止します。", QtGui.QMessageBox.Ok)\r
             self.mainframe.mapframe.setoriginemode = False\r
         else:\r
             if QtGui.QMessageBox.Ok == QtGui.QMessageBox.information(\r
-                self, title, u"基準にする地点をクリックしてください。",\r
+                self, title, "基準にする地点をクリックしてください。",\r
                 (QtGui.QMessageBox.Ok| QtGui.QMessageBox.Cancel)):\r
                 self.mainframe.mapframe.setoriginemode = True\r
 \r
@@ -259,8 +288,16 @@ class MainWindow(QtGui.QMainWindow):
             self.mainframe.mapframe.repaint()\r
 \r
     @QtCore.Slot()\r
+    def togglewallmenustring_triggered(self):\r
+        global config\r
+        config["showWallMenuString"] = not config.get("showWallMenuString", False)\r
+        QtGui.QMessageBox.information(\r
+                self, "壁メニューに文字を表示する", "表示の切替は再起動後に有効になります。",\r
+                (QtGui.QMessageBox.Ok))\r
+\r
+    @QtCore.Slot()\r
     def tutorial_triggered(self):\r
-        url = os.path.dirname(os.path.abspath(__file__)) + "/help/index.html"\r
+        url = basedir() + "/help/index.html"\r
         webbrowser.open_new_tab(url)\r
 \r
     @QtCore.Slot()\r
@@ -270,15 +307,17 @@ class MainWindow(QtGui.QMainWindow):
     @QtCore.Slot()\r
     def about_triggered(self):\r
         QtGui.QMessageBox.about(self, "Pydun",\r
-        u"<h1>Pydun.py "+ projectversion + "</h1>"\r
-        u"<p>Copyright (c) 2013 WATAHIKI Hiroyuki</p>"\r
-        u"<p>url: <a href='" + projecturl + "'>" + projecturl + "</a></p>"\r
-        u"<p>e-mail: hrwatahiki at gmail.com</p>"\r
-        u"<p>このソフトウェアはMITライセンスです。</p>"\r
-        u"<p>このソフトウェアは以下のソフトウェアを使用しています。: "\r
-        u"Python, PySide, PyYAML "\r
-        u"これらの作成者に深く感謝いたします。</p>"\r
-        u"<p>詳細はLICENCE.txtを参照してください。</p>")\r
+        "<h1>Pydun.py "+ projectversion + "</h1>"\r
+        "<p>Copyright (c) 2013 WATAHIKI Hiroyuki</p>"\r
+        "<p>url: <a href='" + projecturl + "'>" + projecturl + "</a></p>"\r
+        "<p>e-mail: hrwatahiki at gmail.com</p>"\r
+        "<p>twitter: <a href='https://twitter.com/hrwatahiki'>@hrwatahiki</a></p>"\r
+        "<p>blog: <a href='http://hrwatahiki.blogspot.jp/'>作業記録</a></p>"\r
+        "<p>このソフトウェアはMITライセンスです。</p>"\r
+        "<p>このソフトウェアは以下のソフトウェアを使用しています。: "\r
+        "Python, PySide, PyYAML "\r
+        "これらの作成者に深く感謝いたします。</p>"\r
+        "<p>詳細はLICENCE.txtを参照してください。</p>")\r
 \r
 \r
 class MainFrame(QtGui.QFrame):\r
@@ -293,30 +332,30 @@ class MainFrame(QtGui.QFrame):
 \r
         self.detail = QtGui.QLabel(self)\r
         self.detail.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)\r
-        self.detail.setText(u"")\r
+        self.detail.setText("")\r
         self.detail.setMaximumHeight(100)\r
         self.detail.setMinimumHeight(100)\r
 \r
         self.boxdrawbutton = QtGui.QRadioButton(self)\r
-        self.boxdrawbutton.setText(u"ボックス形式で壁を描画(&B)")\r
+        self.boxdrawbutton.setText("ボックス形式で壁を描画(&B)")\r
         self.boxdrawbutton.setChecked(True)\r
         self.boxdrawbutton.setSizePolicy(\r
             QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)\r
 \r
         self.growdrawbutton = QtGui.QRadioButton(self)\r
-        self.growdrawbutton.setText(u"足跡形式で壁を描画(&G)")\r
+        self.growdrawbutton.setText("足跡形式で壁を描画(&G)")\r
         self.growdrawbutton.setChecked(False)\r
         self.growdrawbutton.setSizePolicy(\r
             QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)\r
 \r
         self.backcolorbutton = QtGui.QRadioButton(self)\r
-        self.backcolorbutton.setText(u"背景色(&C)")\r
+        self.backcolorbutton.setText("背景色(&C)")\r
         self.backcolorbutton.setChecked(False)\r
         self.backcolorbutton.setSizePolicy(\r
             QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)\r
 \r
         self.setbackcolorbutton = QtGui.QPushButton(self)\r
-        self.setbackcolorbutton.setText(u"背景色を設定(&S)...")\r
+        self.setbackcolorbutton.setText("背景色を設定(&S)...")\r
         self.setbackcolorbutton.setSizePolicy(\r
             QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)\r
 \r
@@ -325,6 +364,12 @@ class MainFrame(QtGui.QFrame):
         self.backcolorbox.setSizePolicy(\r
             QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)\r
 \r
+        latestversion = getlatestversion()\r
+        if latestversion != projectversion:\r
+            self.update = QtGui.QLabel(self)\r
+            self.update.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)\r
+            self.update.setText("<a href='{url}'>最新のPydun({ver})がダウンロードできます。</a>".format(url=projecturl, ver=latestversion))\r
+            self.update.setOpenExternalLinks(True)\r
 \r
         layout = QtGui.QGridLayout(self)\r
         layout.addWidget(scrollarea, 0, 0, 1, 3)\r
@@ -334,6 +379,8 @@ class MainFrame(QtGui.QFrame):
         layout.addWidget(self.backcolorbutton, 3, 1, 1, 2)\r
         layout.addWidget(self.setbackcolorbutton, 4, 1, 1, 1)\r
         layout.addWidget(self.backcolorbox, 4, 2, 1, 1)\r
+        if latestversion != projectversion:\r
+            layout.addWidget(self.update, 5, 0, 1, 3)\r
 \r
         self.setLayout(layout)\r
 \r
@@ -351,7 +398,7 @@ class MainFrame(QtGui.QFrame):
     def create_wall_menu(self, direction):\r
         menu = QtGui.QMenu(self)\r
         for idx, img in enumerate(_mapimages.wall_icons):\r
-            act = QtGui.QAction(self)\r
+            act = QtGui.QAction(_mapimages.wall_texts[idx][direction], self)\r
             act.setIcon(img[direction])\r
 \r
             def triggerd(idx):\r
@@ -365,7 +412,7 @@ class MainFrame(QtGui.QFrame):
 \r
     @QtCore.Slot(int, int, int)\r
     def mouse_moved(self, x=0, y=0, b=QtCore.Qt.MouseButton.NoButton):\r
-        cood = u"({x}, {y})\n".format(x=_mapengine.viewx(x), y=_mapengine.viewy(y))\r
+        cood = "({x}, {y})\n".format(x=_mapengine.viewx(x), y=_mapengine.viewy(y))\r
         self.detail.setText(cood + _mapengine.getdetail(x, y))\r
         self.mapframe.repaint()\r
 \r
@@ -649,20 +696,20 @@ class DetailDialog(QtGui.QDialog):
 \r
         marklabel = QtGui.QLabel(self)\r
         marklabel.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)\r
-        marklabel.setText(u"マーク(&M)")\r
+        marklabel.setText("マーク(&M)")\r
         marklabel.setSizePolicy(\r
             QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)\r
 \r
         self.marktext = QtGui.QLineEdit(self)\r
         self.marktext.setMaxLength(1)\r
-        self.marktext.setText(u"")\r
+        self.marktext.setText("")\r
         self.marktext.setMinimumWidth(20)\r
         self.marktext.setSizePolicy(\r
             QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)\r
         marklabel.setBuddy(self.marktext)\r
 \r
         self.forecolorbutton = QtGui.QPushButton(self)\r
-        self.forecolorbutton.setText(u"文字色(&C)...")\r
+        self.forecolorbutton.setText("文字色(&C)...")\r
         self.forecolorbutton.clicked.connect(self.forecolorbutton_clicked)\r
 \r
         self.forecolorbox = ColorBox(self)\r
@@ -672,16 +719,18 @@ class DetailDialog(QtGui.QDialog):
 \r
         detaillabel = QtGui.QLabel(self)\r
         detaillabel.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignRight)\r
-        detaillabel.setText(u"詳細(&D)")\r
+        detaillabel.setText("詳細(&D)")\r
 \r
         self.detailtext = QtGui.QTextEdit(self)\r
-        self.detailtext.setText(u"")\r
+        self.detailtext.setText("")\r
         detaillabel.setBuddy(self.detailtext)\r
 \r
-        self.buttonBox = QtGui.QDialogButtonBox(\r
+        self.buttonbox = QtGui.QDialogButtonBox(\r
             QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)\r
-        self.buttonBox.accepted.connect(self.accept)\r
-        self.buttonBox.rejected.connect(self.reject)\r
+        self.buttonbox.accepted.connect(self.accept)\r
+        self.buttonbox.rejected.connect(self.reject)\r
+        self.buttonbox.button(QtGui.QDialogButtonBox.Ok).setText("OK")\r
+        self.buttonbox.button(QtGui.QDialogButtonBox.Cancel).setText("キャンセル")\r
 \r
         layout = QtGui.QGridLayout()\r
         layout.addWidget(marklabel, 0, 0, 1, 1)\r
@@ -690,12 +739,12 @@ class DetailDialog(QtGui.QDialog):
         layout.addWidget(self.forecolorbox, 0, 3, 1, 1)\r
         layout.addWidget(detaillabel, 1, 0, 1, 1)\r
         layout.addWidget(self.detailtext, 1, 1, 1, 3)\r
-        layout.addWidget(self.buttonBox, 2, 0, 1, 4)\r
+        layout.addWidget(self.buttonbox, 2, 0, 1, 4)\r
         self.setLayout(layout)\r
         self.setModal(True)\r
 \r
     def setvalue(self, x, y, mark, detail, color):\r
-        self.setWindowTitle("({x},{y})".format(x=x, y=y))\r
+        self.setWindowTitle("({x}, {y})".format(x=x, y=y))\r
         self.marktext.setText(mark)\r
         self.detailtext.setText(detail)\r
         self.forecolorbox.color = color\r
@@ -709,25 +758,26 @@ class DetailDialog(QtGui.QDialog):
         if dlg.result() == QtGui.QDialog.Accepted:\r
             self.forecolorbox.color = dlg.currentColor()\r
 \r
+\r
 class SetOrigineDialog(QtGui.QDialog):\r
     def __init__(self, parent=None):\r
         super(SetOrigineDialog, self).__init__(parent)\r
-        self.setWindowTitle(u"座標設定")\r
+        self.setWindowTitle("座標設定")\r
 \r
         promptlabel = QtGui.QLabel(self)\r
         promptlabel.setAlignment(\r
             QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft)\r
-        promptlabel.setText(u"この地点の座標を入力してください。")\r
+        promptlabel.setText("この地点の座標を入力してください。")\r
 \r
         self.currentlabel = QtGui.QLabel(self)\r
         self.currentlabel.setAlignment(\r
             QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter)\r
-        self.currentlabel.setText(u"")\r
+        self.currentlabel.setText("")\r
 \r
         xlabel = QtGui.QLabel(self)\r
         xlabel.setAlignment(\r
             QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)\r
-        xlabel.setText(u"&X")\r
+        xlabel.setText("&X")\r
 \r
         self.xbox = QtGui.QSpinBox(self)\r
         self.xbox.setRange(-999, +999)\r
@@ -737,7 +787,7 @@ class SetOrigineDialog(QtGui.QDialog):
 \r
         ylabel = QtGui.QLabel(self)\r
         ylabel.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignRight)\r
-        ylabel.setText(u"&Y")\r
+        ylabel.setText("&Y")\r
 \r
         self.ybox = QtGui.QSpinBox(self)\r
         self.ybox.setRange(-999, +999)\r
@@ -745,10 +795,12 @@ class SetOrigineDialog(QtGui.QDialog):
         self.ybox.setValue(0)\r
         ylabel.setBuddy(self.ybox)\r
 \r
-        self.buttonBox = QtGui.QDialogButtonBox(\r
+        self.buttonbox = QtGui.QDialogButtonBox(\r
             QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)\r
-        self.buttonBox.accepted.connect(self.accept)\r
-        self.buttonBox.rejected.connect(self.reject)\r
+        self.buttonbox.accepted.connect(self.accept)\r
+        self.buttonbox.rejected.connect(self.reject)\r
+        self.buttonbox.button(QtGui.QDialogButtonBox.Ok).setText("OK")\r
+        self.buttonbox.button(QtGui.QDialogButtonBox.Cancel).setText("キャンセル")\r
 \r
         layout = QtGui.QGridLayout()\r
         layout.addWidget(promptlabel, 0, 0, 1, 4)\r
@@ -757,14 +809,14 @@ class SetOrigineDialog(QtGui.QDialog):
         layout.addWidget(self.xbox, 2, 1, 1, 1)\r
         layout.addWidget(ylabel, 2, 2, 1, 1)\r
         layout.addWidget(self.ybox, 2, 3, 1, 1)\r
-        layout.addWidget(self.buttonBox, 3, 0, 1, 4)\r
+        layout.addWidget(self.buttonbox, 3, 0, 1, 4)\r
         self.setLayout(layout)\r
         self.setModal(True)\r
 \r
     def setcurrent(self, x, y):\r
         self.xbox.setValue(x)\r
         self.ybox.setValue(y)\r
-        self.currentlabel.setText(u"現在の座標 ({x}, {y})".format(x=x, y=y))\r
+        self.currentlabel.setText("現在の座標 ({x}, {y})".format(x=x, y=y))\r
 \r
     @property\r
     def originex(self):\r
@@ -778,10 +830,10 @@ class SetOrigineDialog(QtGui.QDialog):
 class SetSizeDialog(QtGui.QDialog):\r
     def __init__(self, parent=None):\r
         super(SetSizeDialog, self).__init__(parent)\r
-        self.setWindowTitle(u"マップのサイズ")\r
+        self.setWindowTitle("マップのサイズ")\r
 \r
         self.topbutton = QtGui.QRadioButton(self)\r
-        self.topbutton.setText(u"上(&T)")\r
+        self.topbutton.setText("上(&T)")\r
         self.topbutton.clicked.connect(self.updatewidgets)\r
 \r
         self.topsize = QtGui.QSpinBox(self)\r
@@ -790,7 +842,7 @@ class SetSizeDialog(QtGui.QDialog):
         self.topsize.valueChanged.connect(self.updatewidgets)\r
 \r
         self.bottombutton = QtGui.QRadioButton(self)\r
-        self.bottombutton.setText(u"下(&B)")\r
+        self.bottombutton.setText("下(&B)")\r
         self.bottombutton.clicked.connect(self.updatewidgets)\r
 \r
         self.bottomsize = QtGui.QSpinBox(self)\r
@@ -799,7 +851,7 @@ class SetSizeDialog(QtGui.QDialog):
         self.bottomsize.valueChanged.connect(self.updatewidgets)\r
 \r
         self.leftbutton = QtGui.QRadioButton(self)\r
-        self.leftbutton.setText(u"左(&L)")\r
+        self.leftbutton.setText("左(&L)")\r
         self.leftbutton.clicked.connect(self.updatewidgets)\r
 \r
         self.leftsize = QtGui.QSpinBox(self)\r
@@ -808,7 +860,7 @@ class SetSizeDialog(QtGui.QDialog):
         self.leftsize.valueChanged.connect(self.updatewidgets)\r
 \r
         self.rightbutton = QtGui.QRadioButton(self)\r
-        self.rightbutton.setText(u"右(&R)")\r
+        self.rightbutton.setText("右(&R)")\r
         self.rightbutton.clicked.connect(self.updatewidgets)\r
 \r
         self.rightsize = QtGui.QSpinBox(self)\r
@@ -819,12 +871,14 @@ class SetSizeDialog(QtGui.QDialog):
         self.sizelabel = QtGui.QLabel(self)\r
         self.sizelabel .setAlignment(\r
             QtCore.Qt.AlignVCenter | QtCore.Qt.AlignLeft)\r
-        self.sizelabel.setText(u"この地点の座標を入力してください。")\r
+        self.sizelabel.setText("この地点の座標を入力してください。")\r
 \r
         self.buttonbox = QtGui.QDialogButtonBox(\r
         QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)\r
         self.buttonbox.accepted.connect(self.accept)\r
         self.buttonbox.rejected.connect(self.reject)\r
+        self.buttonbox.button(QtGui.QDialogButtonBox.Ok).setText("OK")\r
+        self.buttonbox.button(QtGui.QDialogButtonBox.Cancel).setText("キャンセル")\r
 \r
         verticalgroup = QtGui.QButtonGroup(self)\r
         verticalgroup.addButton(self.topbutton)\r
@@ -884,7 +938,7 @@ class SetSizeDialog(QtGui.QDialog):
             self.rightsize.setEnabled(True)\r
 \r
         self.sizelabel.setText(\r
-            u"変更前のサイズ: {w1} x {h1}\n変更後のサイズ: {w2} x {h2}".format(\r
+            "変更前のサイズ: {w1} x {h1}\n変更後のサイズ: {w2} x {h2}".format(\r
                 w1=self._width, h1=self._height,\r
                 w2=self._width+dw, h2=self._height+dh))\r
 \r
@@ -905,21 +959,31 @@ class SetSizeDialog(QtGui.QDialog):
 \r
 \r
 class MapImages(object):\r
-    def __init__(self):\r
+    def __init__(self, show_wall_menu_string):\r
+        if show_wall_menu_string:\r
+            vtext = ["なし", "壁", "扉", "扉(→)", "扉(←)", "一通(→)", "一通(←)", "隠", "隠(→)", "隠(←)",]\r
+            htext = ["なし", "壁", "扉", "扉(↓)", "扉(↑)", "一通(↓)", "一通(↑)", "隠", "隠(↓)", "隠(↑)",]\r
+        else:\r
+            vtext = ["", "", "", "", "", "", "", "", "", "",]\r
+            htext = ["", "", "", "", "", "", "", "", "", "",]\r
         self.wall_images = list()\r
         self.wall_icons = list()\r
+        self.wall_texts = list()\r
         for index in range(10):\r
             self.wall_images.append(dict())\r
             self.wall_icons.append(dict())\r
+            self.wall_texts.append(dict())\r
             for direction in ["v", "h"]:\r
                 filename = os.path.join(\r
-                    os.path.dirname(os.path.abspath(__file__)),\r
-                    u"images",\r
-                    u"wall_{direction}_{index:02}.png".format(\r
+                    basedir(),\r
+                    "images",\r
+                    "wall_{direction}_{index:02}.png".format(\r
                         direction=direction, index=index))\r
                 self.wall_images[index][direction] = QtGui.QImage()\r
                 self.wall_images[index][direction].load(filename)\r
                 self.wall_icons[index][direction] = QtGui.QIcon(filename)\r
+            self.wall_texts[index]["v"] = vtext[index]\r
+            self.wall_texts[index]["h"] = htext[index]\r
 \r
     @property\r
     def width(self):\r
@@ -978,8 +1042,8 @@ class MapEngine(object):
         def represent_unicode(dumper, data):\r
             return dumper.represent_scalar("tag:yaml.org,2002:str", data)\r
         def construct_unicode(loader, node):\r
-            return unicode(loader.construct_scalar(node))\r
-        yaml.add_representer(unicode, represent_unicode)\r
+            return str(loader.construct_scalar(node))\r
+        yaml.add_representer(str, represent_unicode)\r
         yaml.add_constructor("tag:yaml.org,2002:str", construct_unicode)\r
 \r
     def getdata(self, x, y, direction):\r
@@ -1030,13 +1094,13 @@ class MapEngine(object):
 \r
     def getnote(self, x, y):\r
         return self._note.get(\r
-            self.coodtokey(x, y), {"mark":u"", "detail":u"", "forecolor":u"#000000", "backcolor":u""})\r
+            self.coodtokey(x, y), {"mark":"", "detail":"", "forecolor":"#000000", "backcolor":""})\r
 \r
     def coodtokey(self, x, y):\r
-        return u"{x:+05d}_{y:+05d}".format(x=x, y=y)\r
+        return "{x:+05d}_{y:+05d}".format(x=int(x), y=int(y))\r
 \r
     def keytocood(self, key):\r
-        return map(int, key.split("_"))\r
+        return list(map(int, key.split("_")))\r
 \r
     def setmark(self, x, y, mark):\r
         note = self.getnote(x, y)\r
@@ -1172,7 +1236,7 @@ class MapEngine(object):
 \r
     def save(self, filename):\r
         dt = self.savestring()\r
-        with codecs.open(filename, "w") as f:\r
+        with open(filename, "w", encoding='utf-8') as f:\r
             f.write(dt)\r
             self.filename = filename\r
 \r
@@ -1185,13 +1249,13 @@ class MapEngine(object):
 \r
         #noteは表示用に座標変換する。\r
         n = dict()\r
-        for nk, ni in self._note.items():\r
+        for nk, ni in list(self._note.items()):\r
             if ni["mark"] != "" or ni["detail"] != "" or ni["backcolor"]:\r
                 x, y = self.keytocood(nk)\r
                 n[self.coodtokey(self.viewx(x), self.viewy(y))] = ni\r
         data["note"] = n\r
         return yaml.safe_dump(data, allow_unicode=True,\r
-                default_flow_style=False, encoding='utf-8')\r
+                default_flow_style=False, encoding='utf-8').decode('utf-8')\r
 \r
     def getmapstring(self):\r
         #出力用マップ作成\r
@@ -1222,7 +1286,7 @@ class MapEngine(object):
         return m\r
 \r
     def load(self, filename):\r
-        with codecs.open(filename, "r", encoding="utf-8") as f:\r
+        with open(filename, "r", encoding="utf-8") as f:\r
             st = f.read()\r
         self.loadfromstring(st)\r
         self.filename = filename\r
@@ -1253,7 +1317,7 @@ class MapEngine(object):
 \r
         #noteは内部用に座標変換する。\r
         n = dict()\r
-        for nk, ni in data["note"].items():\r
+        for nk, ni in list(data["note"].items()):\r
             if ni["mark"] != "" or ni["detail"] != "" or ni["backcolor"] != "":\r
                 x, y = self.keytocood(nk)\r
                 n[self.coodtokey(self.worldx(x), self.worldy(y))] = ni\r
@@ -1273,8 +1337,14 @@ class UndoManager(QtCore.QObject):
         self._undo = [None for x in range(self.MAX_UNDO_COUNT)]\r
         self._index = 0\r
         self._undocount = 0\r
+        self._commited = True\r
         self.changed.emit(self.canundo, self.canredo)\r
 \r
+    def init(self, data):\r
+        self.clear()\r
+        self.save(data)\r
+        self._commited = True\r
+\r
     def save(self, obj):\r
         if self._index >= self.MAX_UNDO_COUNT:\r
             self._undo = self._undo[1:]\r
@@ -1283,20 +1353,26 @@ class UndoManager(QtCore.QObject):
         self._undo[self._index] = obj\r
         self._index += 1\r
         self._undocount = 0\r
+        self._commited = False\r
         self.changed.emit(self.canundo, self.canredo)\r
 \r
     def undo(self):\r
         self._index -= 1\r
         self._undocount += 1\r
+        self._commited = False\r
         self.changed.emit(self.canundo, self.canredo)\r
         return self._undo[self._index - 1]\r
 \r
     def redo(self):\r
         self._index += 1\r
         self._undocount -= 1\r
+        self._commited = False\r
         self.changed.emit(self.canundo, self.canredo)\r
         return self._undo[self._index - 1]\r
 \r
+    def commit(self):\r
+        self._commited = True\r
+\r
     @property\r
     def canundo(self):\r
         return (self._index > 1)\r
@@ -1305,6 +1381,10 @@ class UndoManager(QtCore.QObject):
     def canredo(self):\r
         return (self._undocount > 0)\r
 \r
+    @property\r
+    def commited(self):\r
+        return self._commited\r
+\r
 \r
 class PydunColorDialog(QtGui.QColorDialog):\r
     def __init__(self, parent, config):\r
@@ -1330,6 +1410,17 @@ class PydunColorDialog(QtGui.QColorDialog):
         return self._config\r
 \r
 \r
+class PydunAskSaveDialog(QtGui.QMessageBox):\r
+    def __init__(self, parent, filename):\r
+        super(PydunAskSaveDialog, self).__init__(parent)\r
+        self.setText("{filename} への変更を保存しますか?".format(filename=filename))\r
+        self.setStandardButtons(QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard | QtGui.QMessageBox.Cancel)\r
+        self.setDefaultButton(QtGui.QMessageBox.Save)\r
+        self.button(QtGui.QMessageBox.Save).setText("保存する(&S)")\r
+        self.button(QtGui.QMessageBox.Discard).setText("保存しない(&N)")\r
+        self.button(QtGui.QMessageBox.Cancel).setText("キャンセル")\r
+\r
+\r
 def getcolorstring(color):\r
     return "#{r:02x}{g:02x}{b:02x}".format(r=color.red(), g=color.green(), b=color.blue())\r
 \r
@@ -1339,6 +1430,21 @@ def getcolorfromstring(colorstring):
         int(colorstring[3:5], 16),\r
         int(colorstring[5:7], 16))\r
 \r
+def basedir():\r
+    return os.path.dirname(os.path.abspath(sys.argv[0]))\r
+\r
+def getlatestversion():\r
+    try:\r
+        rss = urllib.request.urlopen(projectrssurl)\r
+        rssstring = rss.read()\r
+        rsstree = xml.etree.ElementTree.fromstring(rssstring)\r
+        item = rsstree.find("channel/item/title")\r
+        ver = (item.text.split(" "))[2]\r
+        rss.close()\r
+    except:\r
+        ver = projectversion\r
+    return ver\r
+\r
 \r
 def main():\r
     loadconfig()\r
@@ -1352,8 +1458,8 @@ def loadconfig():
     global config\r
     global configfilename\r
     configfilename = os.path.join(\r
-        os.path.dirname(os.path.abspath(__file__)),\r
-        u"Pydun.config")\r
+        basedir(),\r
+        "Pydun.config")\r
     try:\r
         with open(configfilename, "r") as f:\r
             config = yaml.safe_load(f)\r