# GNU Solfege - eartraining for GNOME
# Copyright (C) 2000, 2001, 2002  Tom Cato Amundsen
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

from __future__ import nested_scopes

import sys
import time, random
from i18n import _
import configureoutput

def do_some_cmdline_options(argv):
        if '-h' in argv or '--help' in argv:
            print """
GNU Solfege %s
Type "man solfege" for more details.
Options:
  --no-sound             Do not play any sounds. Useful when porting.
  --verbose-sound-init   display more info about the sound setup
  -v, --version
  -w, --warranty         show warranty and copyright
  -h, --help             Display this message
  --gtk-help             Display the usual gtk/gnome commandline options.
    """ % configureoutput.VERSION_STRING
            sys.exit()

        if '-v' in argv or '--version' in argv:
            print """GNU Solfege %s
This is free software. It is covered by the GNU General Public License,
and you are welcome to change it and/or distribute copies of it under
certain conditions. Invoke as `solfege --warranty` for more information.

Copyright (C) 2000, 2001, 2002  Tom Cato Amundsen <tca@gnu.org>
            """ % configureoutput.VERSION_STRING
            sys.exit()

        if '-w' in argv or '--warranty' in argv:
            print """GNU Solfege %s
Copyright (C) 2000, 2001, 2002  Tom Cato Amundsen <tca@gnu.org>

    This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

    You should have received a copy (refer to the file COPYING) of the
GNU General Public License along with this program; if not, write to
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139,
USA.
        """ % configureoutput.VERSION_STRING
            sys.exit()


do_some_cmdline_options(sys.argv)

if '--gtk-help' in sys.argv:
    del sys.argv[sys.argv.index('--gtk-help')]
    sys.argv.append('--help')

# silent, GNOME, be silent!
sys.argv.append('--disable-sound')

import locale
import gtk

gtk.rc_parse("solfege.gtkrc")

class SplashWin(gtk.GtkWindow):
    def __init__(self):
        gtk.GtkWindow.__init__(self, gtk.WINDOW_POPUP)
        self.set_position(gtk.WIN_POS_CENTER)
        self.set_policy(gtk.FALSE, gtk.FALSE, gtk.FALSE)
        self.set_name("SplashWindow")
        frame = gtk.GtkFrame()
        frame.set_shadow_type(gtk.SHADOW_OUT)
        self.add(frame)
        vbox = gtk.GtkVBox()
        vbox.set_border_width(20)
        frame.add(vbox)
        l = gtk.GtkLabel(_("Starting GNU Solfege %s") % configureoutput.VERSION_STRING)
        l.set_name("Heading1")
        vbox.pack_start(l)
        l = gtk.GtkLabel("http://www.solfege.org")
        vbox.pack_start(l)
        self.show_all()

if not filter(lambda a: a in sys.argv, ['--no-splash', '--usage', '-?']):
    splash_win = SplashWin()
    time.sleep(0.1)
    gtk.gdk_flush()
    while gtk.events_pending():
        gtk.mainiteration(0)
else:
    splash_win = None
    if '--no-splash' in sys.argv:
        del sys.argv[sys.argv.index('--no-splash')]

# we have to remove all options gnome/gtk don't understand
# from sys.argv
args = []
for a in ('--no-sound', '--verbose-sound-init'):
    if a in sys.argv:
        args.append(a)
        del sys.argv[sys.argv.index(a)]

import gnome, gnome.ui
# FIXME
from gnome.ui import *
import GDK, os, const, string
locale.setlocale(locale.LC_NUMERIC, "C")
import app
from htmlwidget import HtmlWidget
import utils, history
from configwindow import ConfigWindow
import mpd, mpd.musicdisplayer, gu, cfg
import tracebackwindow


# redirect error messages to a window that will popup if
# something bad happens.

w = tracebackwindow.TracebackWindow()
sys.stderr = w


class DocViewer(gtk.GtkVBox):
    def __init__(self, activate_cb):
        gtk.GtkVBox.__init__(self)
        self.m_htmlwidget = HtmlWidget(activate_cb, self.on_anchor_track)
        self.m_htmlwidget.show_all()
        self.pack_start(self.m_htmlwidget)
        self.set_usize(300, 300)
        self.read_docfile = self.m_htmlwidget.read_docfile
        self.on_key_press_event = self.m_htmlwidget.on_key_press_event
        self.m_statusbar = gtk.GtkStatusbar()
        self.m_statusbar.show()
        self.pack_start(self.m_statusbar, gtk.FALSE)
        self.m_link_under_mouse = None
        self.source = self.m_htmlwidget.source
    def on_anchor_track(self, url):
        if url:
            # remove newlines in url because it make the window resize
            s = string.replace(url, "\n", "")
            # we have to use m_link_under_mouse because XmHTML does not
            # always emit the correct signals. Try to go without this
            # when we switch to GtkHTML
            if self.m_link_under_mouse:
                self.m_statusbar.pop(1)
            self.m_statusbar.push(1, s)
            self.m_link_under_mouse = 1
        else:
            self.m_statusbar.pop(1)
            self.m_link_under_mouse = None


class MusicViewerWindow(gtk.GtkDialog):
    def __init__(self, app):
        gtk.GtkDialog.__init__(self)
        self.m_app = app
        self.set_usize(400, 200)
        self.g_music_displayer = mpd.musicdisplayer.MusicDisplayer(utils.play_tone)
        self.vbox.pack_start(self.g_music_displayer)
        gu.bButton(self.action_area, _("Close"), self.m_app.close_musicviewer)
        self.connect('destroy', self.m_app.close_musicviewer)
        self.show_all()
    def display_music(self, music):
        fontsize = cfg.get_int('config/feta_font_size=20')
        self.g_music_displayer.display(music, fontsize)

class MainWin(gnome.ui.GnomeApp, cfg.ConfigUtils):
    def __init__(self, argv):
        gnome.ui.GnomeApp.__init__(self,
          'solfege%i.%i' % (configureoutput.MAJOR_VERSION,
                            configureoutput.MINOR_VERSION), 'Solfege')
        cfg.ConfigUtils.__dict__['__init__'](self, 'mainwin')
        if self.get_bool('gui/mainwin_user_resizeable'):
            self.set_policy(0, 1, 0)
        else:
            self.set_policy(0, 0, 1)
        self.connect('destroy', self.quit_program)
        self.connect('key_press_event', self.on_key_press_event)
        self.g_about_window = None
        self.main_box = gtk.GtkVBox()
        self.main_box.show()
        self.set_contents(self.main_box)
        self.m_exercise = None
        self.m_viewer = None
        self.box_dict = {}
        self.g_config_window = None
        self.g_musicviewer_window = None
        self.m_history = history.History()
        self.m_key_bindings = {'history_back_ak': self.history_back,
                               'history_forward_ak': self.history_forward,
                               'history_reload_ak': self.history_reload}
        self.m_app = app.SolfegeApp(argv, self)
        self.setup_menubar()
        self.setup_toolbars()
        if configureoutput.WITH_GNOME:
            self.m_app.handle_href('welcome.html')
        else:
            self.m_app.handle_href('nognomewelcome.html')
    def display_error_message(self, msg):
        gnome.ui.GnomeErrorDialog(msg)
    def show_about_window(self, widget):
        if not self.g_about_window:
            self.g_about_window = gnome.ui.GnomeAbout(
                    'GNU Solfege', configureoutput.VERSION_STRING,
                    'Copyright (C) 2000, 2001, 2002  Tom Cato Amundsen',
                    ['Tom Cato Amundsen',
                     'Oliver Vollmer %s' % _("(toolbar icons)"),
                     'Giovanni Chierico %s' % _("(some lessonfiles)"),
                     'Joe Lee %s' % _("(sound code for the MS Windows port)"),
                     ],
                    #['see AUTHORS file or online documentation for details'],
                    _("GPL'ed eartraining.") + "\n"
                    + _("New version available at http://www.solfege.org"))
            self.g_about_window.connect('destroy', self.hide_about_window)
            self.g_about_window.show()
    def hide_about_window(self, widget):
        self.g_about_window.destroy()
        self.g_about_window = None
    def post_constructor(self):
        if not self.get_bool("gui/navbar_visible"):
            self.get_dock_item_by_name('backandforward').hide()
        if not self.get_bool("gui/toolbar_visible"):
            self.get_dock_item_by_name('main-toolbar').hide()
    def activate_exercise(self, href):
        protocol, action, exname, collection, lessonfile, config = \
           utils.parse_url(href)
        assert protocol == 'solfege'
        if self.m_viewer:
            if self.m_viewer != 'docviewer':
                self.box_dict[self.m_viewer].on_end_practise()
            self.box_dict[self.m_viewer].hide()
        self.box_dict[exname].show()
        self.box_dict[exname].on_start_practise()
        self.m_viewer = exname
        if action in ['practise', 'config', 'statistics']:
            # We need this test because not all exercises use a notebook.
            if self.box_dict[self.m_viewer].g_notebook:
                self.box_dict[self.m_viewer].g_notebook.set_page(
                   ['practise', 'config', 'statistics'].index(action))
        self.set_title("Solfege - " + self.box_dict[exname].get_pretty_name())
        self.m_history.add(href)
    def _show_docviewer(self):
        if not self.box_dict.has_key('docviewer'):
            self.box_dict['docviewer'] = DocViewer(self.m_app.handle_href)
            self.main_box.pack_start(self.box_dict['docviewer'])
        if self.m_viewer and (self.m_viewer != 'docviewer'):
                self.box_dict[self.m_viewer].hide()
        self.m_viewer = 'docviewer'
        self.box_dict['docviewer'].show()
    def display_docfile(self, fn):
        self._show_docviewer()
        self.box_dict['docviewer'].read_docfile(fn)
        self.set_title("Solfege - %s" % fn)
        self.m_history.add(fn)
    def display_html(self, html):
        self._show_docviewer()
        self.box_dict['docviewer'].source(html)
    def history_back(self, widget=None):
        self.m_history.back()
        self.m_history.lock()
        self.m_app.handle_href(self.m_history.get_current())
        self.m_history.unlock()
    def history_forward(self, widget=None):
        self.m_history.forward()
        self.m_history.lock()
        self.m_app.handle_href(self.m_history.get_current())
        self.m_history.unlock()
    def history_reload(self, widget=None):
        self.m_history.lock()
        self.m_app.handle_href(self.m_history.get_current())
        self.m_history.unlock()
    def initialise_exercise(self, teacher):
        assert not self.box_dict.has_key(teacher.m_exname)
        n = utils.exercise_name_to_module_name(teacher.m_exname)
        exec("import %s" % n)
        self.box_dict[teacher.m_exname] = locals()[n].Gui(teacher, self)
        self.main_box.pack_start(self.box_dict[teacher.m_exname])
    def on_key_press_event(self, widget, event):
        if event.type == GDK.KEY_PRESS:
            for s in self.m_key_bindings.keys():
                if (event.state, event.keyval) \
                         == utils.parse_key_string(self.get_string(s)):
                    self.m_key_bindings[s]()
                    return 1
        self.box_dict[self.m_viewer].on_key_press_event(widget, event)
    def open_preferences_window(self, widget=None):
        if not self.g_config_window:
            self.g_config_window = ConfigWindow(self.m_app)
            self.g_config_window.set_parent(self)
            self.g_config_window.show()
        else:
            self.g_config_window.show()
    def setup_menubar(self):
        file_menu = []
        for e in const.exercises:
            file_menu.append(
             (APP_UI_ITEM, const.exercisedata[e][0], None,
              lambda w, e=e: self.m_app.handle_href("solfege:practise/"+e),
              None, APP_PIXMAP_NONE, None,
              const.exercisedata[e][2], const.exercisedata[e][3]))
        file_menu = file_menu + [
            UIINFO_SEPARATOR,
            UIINFO_MENU_PREFERENCES_ITEM(self.open_preferences_window),
            UIINFO_MENU_EXIT_ITEM(self.quit_program),
            ]
        help_menu = [
                    (APP_UI_ITEM, _("_Help on current exercise"), None,
                     lambda w: self.m_app.please_help_me(),
                     None, APP_PIXMAP_NONE, None, GDK.F1, 0),
                    (APP_UI_ITEM, _("Welcome"), None,
                    lambda w: self.m_app.handle_href('welcome.html'),
                    None, APP_PIXMAP_NONE, None, 0, 0),
                    (APP_UI_ITEM, _("All help files"), None,
                    lambda w: self.m_app.handle_href('index.html'),
                    None, APP_PIXMAP_NONE, None, 0, 0),
                    (APP_UI_ITEM, _("All installed lesson files"), None,
                    lambda w: self.m_app.handle_href('solfege:all-lessonfiles'),
                    None, APP_PIXMAP_NONE, None, 0, 0),
                    UIINFO_SEPARATOR,
                    (APP_UI_ITEM, _("Copyright notice"), None,
                    lambda w: self.m_app.handle_href('copyright.html'),
                    None, APP_PIXMAP_NONE, None, 0, 0),
                    (APP_UI_ITEM, _("Credits"), None,
                    lambda w: self.m_app.handle_href('AUTHORS.html'),
                    None, APP_PIXMAP_NONE, None, 0, 0),
                    (APP_UI_ITEM, _("Mailinglists, web page etc."), None,
                    lambda w: self.m_app.handle_href('online-resources.html'),
                    None, APP_PIXMAP_NONE, None, 0, 0),
                    (APP_UI_ITEM, _("Reporting bugs"), None,
                    lambda w: self.m_app.handle_href('bug-reporting.html'),
                    None, APP_PIXMAP_NONE, None, 0, 0),
                    UIINFO_MENU_ABOUT_ITEM(self.show_about_window, None),
                    ]
        development_menu =  [
            (APP_UI_ITEM, _("This menu contains\nmostly useless stuff"),
             None, None, None, APP_PIXMAP_NONE, None, 0, 0),
             UIINFO_SEPARATOR,
            (APP_UI_ITEM, _("_Tempo"), None,
             lambda w: self.m_app.handle_href('solfege:/identify-bpm'),
             None, APP_PIXMAP_NONE, None, 0, 0),
            (APP_UI_ITEM, _("Intervall"), None,
             lambda w: self.m_app.handle_href('solfege:/intervalls'),
             None, APP_PIXMAP_NONE, None, 0, 0),
            (APP_UI_ITEM, _("Rhythm"), None,
             lambda w: self.m_app.handle_href('solfege:/polyrhythm'),
             None, APP_PIXMAP_NONE, None, 0, 0),
            (APP_UI_ITEM, const.exercisedata['harmonic-progression-dictation'][0], None,
             lambda w: self.m_app.handle_href('solfege:/harmonic-progression-dictation'),
             None, APP_PIXMAP_NONE, None, 0, 0),

            UIINFO_ITEM_STOCK(_("Experiment"), None,
                              self.experiment, STOCK_MENU_EXEC),
                 ]
        if configureoutput.ENABLE_TUNER:
            development_menu = development_menu + [
            (APP_UI_ITEM, _("Tuner"), None,
             lambda w: self.m_app.handle_href('solfege:/tuner'),
             None, APP_PIXMAP_NONE, None, 0, 0)]
        self.m_menuinfo = menu = [
            UIINFO_SUBTREE(_("_File"), file_menu),
            UIINFO_SUBTREE(_("_Development"), development_menu),
            UIINFO_SUBTREE(_("_Help"), help_menu),
            ]
        self.create_menus(menu)
        if not self.get_bool('gui/development_menu_visible'):
            self.get_dock_item_by_name("Menubar").children()[0].children()[1].hide()
    def experiment(self, w=None):
        pass
    def quit_program(self, w=None):
        self.m_app.quit_program()
        gtk.mainquit()
    def setup_toolbars(self):
        self.g_toolbar = gtk.GtkToolbar(gtk.ORIENTATION_HORIZONTAL,
                                        gtk.TOOLBAR_ICONS)
        self.g_toolbar.set_style({'icons': gtk.TOOLBAR_ICONS,
                                  'text': gtk.TOOLBAR_TEXT,
                                  'both': gtk.TOOLBAR_BOTH}
               [self.get_string('gui/toolbar_style')])
        bg = self.get_style().bg[gtk.STATE_NORMAL]
        for i in const.exercises:
            n = string.replace(const.exercisedata[i][0], '_', '')
            pix, mask = gtk.create_pixmap_from_xpm(self, None,
               os.path.join('xpm', const.exercisedata[i][1]))
            self.g_toolbar.append_item(n, n, i, gtk.GtkPixmap(pix, mask),
                    lambda w, i=i: self.m_app.handle_href("solfege:practise/"+i))
        self.add_toolbar(self.g_toolbar, "main-toolbar",
                         gnome.uiconsts.DOCK_ITEM_BEH_NORMAL,
                         gnome.uiconsts.DOCK_TOP, 1, 1, 0)
        self.g_navbar = gtk.GtkToolbar(gtk.ORIENTATION_HORIZONTAL,
                                       gtk.TOOLBAR_ICONS)
        self.g_navbar.append_item(_("Back"), None, None,
            gnome.ui.GnomeStock(gnome.ui.STOCK_PIXMAP_BACK), self.history_back)
        self.g_navbar.append_item(_("Forward"), None, None,
              gnome.ui.GnomeStock(gnome.ui.STOCK_PIXMAP_FORWARD),
                                  self.history_forward)
        self.g_navbar.set_style({'icons': gtk.TOOLBAR_ICONS,
                                 'text': gtk.TOOLBAR_TEXT,
                                 'both': gtk.TOOLBAR_BOTH}
               [self.get_string('gui/navbar_style')])
        self.add_toolbar(self.g_navbar, 'backandforward',
                         gnome.uiconsts.DOCK_ITEM_BEH_NORMAL,
                         gnome.uiconsts.DOCK_TOP, 1, 0, 0)
    def display_in_musicviewer(self, music):
        if not self.g_musicviewer_window:
            self.g_musicviewer_window = MusicViewerWindow(self)
            self.g_musicviewer_window.show()
        self.g_musicviewer_window.display_music(music)
    def close_musicviewer(self, widget=None):
        self.g_musicviewer_window.destroy()
        self.g_musicviewer_window = None

w = MainWin(args)
w.show()
w.post_constructor()
if splash_win:
    splash_win.destroy()
gtk.mainloop()

