summaryrefslogtreecommitdiff
path: root/apps/plugins/lua/lstring.c
blob: 49113151cc709aa35f469a82a004b37d850d4bf5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/*
** $Id: lstring.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $
** String table (keeps all strings handled by Lua)
** See Copyright Notice in lua.h
*/


#include <string.h>

#define lstring_c
#define LUA_CORE

#include "lua.h"

#include "lmem.h"
#include "lobject.h"
#include "lstate.h"
#include "lstring.h"



void luaS_resize (lua_State *L, int newsize) {
  GCObject **newhash;
  stringtable *tb;
  int i;
  if (G(L)->gcstate == GCSsweepstring)
    return;  /* cannot resize during GC traverse */
  newhash = luaM_newvector(L, newsize, GCObject *);
  tb = &G(L)->strt;
  for (i=0; i<newsize; i++) newhash[i] = NULL;
  /* rehash */
  for (i=0; i<tb->size; i++) {
    GCObject *p = tb->hash[i];
    while (p) {  /* for each node in the list */
      GCObject *next = p->gch.next;  /* save next */
      unsigned int h = gco2ts(p)->hash;
      int h1 = lmod(h, newsize);  /* new position */
      lua_assert(cast_int(h%newsize) == lmod(h, newsize));
      p->gch.next = newhash[h1];  /* chain it */
      newhash[h1] = p;
      p = next;
    }
  }
  luaM_freearray(L, tb->hash, tb->size, TString *);
  tb->size = newsize;
  tb->hash = newhash;
}


static TString *newlstr (lua_State *L, const char *str, size_t l,
                                       unsigned int h) {
  TString *ts;
  stringtable *tb;
  if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char))
    luaM_toobig(L);
  ts = cast(TString *, luaM_malloc(L, (l+1)*sizeof(char)+sizeof(TString)));
  ts->tsv.len = l;
  ts->tsv.hash = h;
  ts->tsv.marked = luaC_white(G(L));
  ts->tsv.tt = LUA_TSTRING;
  ts->tsv.reserved = 0;
  memcpy(ts+1, str, l*sizeof(char));
  ((char *)(ts+1))[l] = '\0';  /* ending 0 */
  tb = &G(L)->strt;
  h = lmod(h, tb->size);
  ts->tsv.next = tb->hash[h];  /* chain new entry */
  tb->hash[h] = obj2gco(ts);
  tb->nuse++;
  if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)
    luaS_resize(L, tb->size*2);  /* too crowded */
  return ts;
}


TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
  GCObject *o;
  unsigned int h = cast(unsigned int, l);  /* seed */
  size_t step = (l>>5)+1;  /* if string is too long, don't hash all its chars */
  size_t l1;
  for (l1=l; l1>=step; l1-=step)  /* compute hash */
    h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1]));
  for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)];
       o != NULL;
       o = o->gch.next) {
    TString *ts = rawgco2ts(o);
    if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) {
      /* string may be dead */
      if (isdead(G(L), o)) changewhite(o);
      return ts;
    }
  }
  return newlstr(L, str, l, h);  /* not found */
}


Udata *luaS_newudata (lua_State *L, size_t s, Table *e) {
  Udata *u;
  if (s > MAX_SIZET - sizeof(Udata))
    luaM_toobig(L);
  u = cast(Udata *, luaM_malloc(L, s + sizeof(Udata)));
  u->uv.marked = luaC_white(G(L));  /* is not finalized */
  u->uv.tt = LUA_TUSERDATA;
  u->uv.len = s;
  u->uv.metatable = NULL;
  u->uv.env = e;
  /* chain it on udata list (after main thread) */
  u->uv.next = G(L)->mainthread->next;
  G(L)->mainthread->next = obj2gco(u);
  return u;
}

>617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2010 Robert Bieber
 *
 * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#include "editorwindow.h"
#include "projectmodel.h"
#include "ui_editorwindow.h"
#include "rbfontcache.h"
#include "rbtextcache.h"
#include "newprojectdialog.h"
#include "projectexporter.h"

#include <QDesktopWidget>
#include <QFileSystemModel>
#include <QSettings>
#include <QFileDialog>
#include <QMessageBox>
#include <QGraphicsScene>
#include <QDir>
#include <QFile>

const int EditorWindow::numRecent = 5;

EditorWindow::EditorWindow(QWidget *parent) :
    QMainWindow(parent), ui(new Ui::EditorWindow), parseTreeSelection(0)
{
    ui->setupUi(this);
    prefs = new PreferencesDialog(this);
    project = 0;
    setupUI();
    loadSettings();
    setupMenus();
}


EditorWindow::~EditorWindow()
{
    delete ui;
    delete prefs;
    if(project)
        delete project;
    delete deviceConfig;
    delete deviceDock;
    delete timer;
    delete timerDock;

    RBFontCache::clearCache();
    RBTextCache::clearCache();
}

void EditorWindow::loadTabFromSkinFile(QString fileName)
{
    docToTop(fileName);

    /* Checking to see if the file is already open */
    for(int i = 0; i < ui->editorTabs->count(); i++)
    {
        TabContent* current = dynamic_cast<TabContent*>
                                (ui->editorTabs->widget(i));
        if(current->file() == fileName)
        {
            ui->editorTabs->setCurrentIndex(i);
            return;
        }
    }

    /* Adding a new document*/
    SkinDocument* doc = new SkinDocument(parseStatus, fileName, project,
                                         deviceConfig);
    addTab(doc);
    ui->editorTabs->setCurrentWidget(doc);

}

void EditorWindow::loadConfigTab(ConfigDocument* doc)
{
    for(int i = 0; i < ui->editorTabs->count(); i++)
    {
        TabContent* current = dynamic_cast<TabContent*>
                              (ui->editorTabs->widget(i));
        if(current->file() == doc->file())
        {
            ui->editorTabs->setCurrentIndex(i);
            doc->deleteLater();
            return;
        }
    }

    addTab(doc);
    ui->editorTabs->setCurrentWidget(doc);

    QObject::connect(doc, SIGNAL(titleChanged(QString)),
                     this, SLOT(tabTitleChanged(QString)));
}

void  EditorWindow::loadSettings()
{

    QSettings settings;

    /* Main Window location */
    settings.beginGroup("EditorWindow");
    QSize size = settings.value("size").toSize();
    QPoint pos = settings.value("position").toPoint();
    QByteArray state = settings.value("state").toByteArray();

    /* Recent docs/projects */
    recentDocs = settings.value("recentDocs", QStringList()).toStringList();
    recentProjects = settings.value("recentProjects",
                                    QStringList()).toStringList();

    settings.endGroup();

    if(!(size.isNull() || pos.isNull() || state.isNull()))
    {
        resize(size);
        move(pos);
        restoreState(state);
    }

}

void EditorWindow::saveSettings()
{

    QSettings settings;

    /* Saving window and panel positions */
    settings.beginGroup("EditorWindow");
    settings.setValue("position", pos());
    settings.setValue("size", size());
    settings.setValue("state", saveState());

    /* Saving recent docs/projects */
    settings.setValue("recentDocs", recentDocs);
    settings.setValue("recentProjects", recentProjects);

    settings.endGroup();
}

void EditorWindow::setupUI()
{
    /* Connecting the tab bar signals */
    QObject::connect(ui->editorTabs, SIGNAL(currentChanged(int)),
                     this, SLOT(shiftTab(int)));
    QObject::connect(ui->editorTabs, SIGNAL(tabCloseRequested(int)),
                     this, SLOT(closeTab(int)));

    /* Connecting the code gen button */
    QObject::connect(ui->fromTree, SIGNAL(pressed()),
                     this, SLOT(updateCurrent()));

    /* Connecting the preferences dialog */
    QObject::connect(ui->actionPreferences, SIGNAL(triggered()),
                     prefs, SLOT(exec()));

    /* Setting up the parse status label */
    parseStatus = new QLabel(this);
    ui->statusbar->addPermanentWidget(parseStatus);

    /* Setting the selection for parse tree highlighting initially NULL */
    parseTreeSelection = 0;

    /* Adding the skin viewer */
    viewer = new SkinViewer(this);
    ui->skinPreviewLayout->addWidget(viewer);

    /* Positioning the device settings dialog */
    deviceDock = new QDockWidget(tr("Device Configuration"), this);
    deviceConfig = new DeviceState(deviceDock);

    deviceDock->setObjectName("deviceDock");
    deviceDock->setWidget(deviceConfig);
    deviceDock->setFloating(true);
    deviceDock->move(QPoint(x() + width() / 2, y() + height() / 2));
    deviceDock->hide();

    /* Positioning the timer panel */
    timerDock = new QDockWidget(tr("Timer"), this);
    timer = new SkinTimer(deviceConfig, timerDock);

    timerDock->setObjectName("timerDock");
    timerDock->setWidget(timer);
    timerDock->setFloating(true);
    timerDock->move(QPoint(x() + width() / 2, y() + height() / 2));
    timerDock->hide();

    shiftTab(-1);
}

void EditorWindow::setupMenus()
{
    /* Adding actions to the toolbar */
    ui->toolBar->addAction(ui->actionNew_Document);
    ui->toolBar->addAction(ui->actionOpen_Document);
    ui->toolBar->addAction(ui->actionSave_Document);
    ui->toolBar->addAction(ui->actionSave_Document_As);

    ui->toolBar->addSeparator();
    ui->toolBar->addAction(ui->actionUndo);
    ui->toolBar->addAction(ui->actionRedo);

    ui->toolBar->addSeparator();
    ui->toolBar->addAction(ui->actionCut);
    ui->toolBar->addAction(ui->actionCopy);
    ui->toolBar->addAction(ui->actionPaste);

    ui->toolBar->addSeparator();
    ui->toolBar->addAction(ui->actionFind_Replace);

    /* Connecting panel show actions */
    QObject::connect(ui->actionFile_Panel, SIGNAL(triggered()),
                     this, SLOT(showPanel()));
    QObject::connect(ui->actionDisplay_Panel, SIGNAL(triggered()),
                     this, SLOT(showPanel()));
    QObject::connect(ui->actionPreview_Panel, SIGNAL(triggered()),
                     this, SLOT(showPanel()));
    QObject::connect(ui->actionDevice_Configuration, SIGNAL(triggered()),
                     deviceDock, SLOT(show()));
    QObject::connect(ui->actionTimer, SIGNAL(triggered()),
                     timerDock, SLOT(show()));

    /* Connecting the document management actions */
    QObject::connect(ui->actionNew_Document, SIGNAL(triggered()),
                     this, SLOT(newTab()));
    QObject::connect(ui->actionNew_Project, SIGNAL(triggered()),
                     this, SLOT(newProject()));

    QObject::connect(ui->actionClose_Document, SIGNAL(triggered()),
                     this, SLOT(closeCurrent()));
    QObject::connect(ui->actionClose_Project, SIGNAL(triggered()),
                     this, SLOT(closeProject()));

    QObject::connect(ui->actionSave_Document, SIGNAL(triggered()),
                     this, SLOT(saveCurrent()));
    QObject::connect(ui->actionSave_Document_As, SIGNAL(triggered()),
                     this, SLOT(saveCurrentAs()));
    QObject::connect(ui->actionExport_Project, SIGNAL(triggered()),
                     this, SLOT(exportProject()));

    QObject::connect(ui->actionOpen_Document, SIGNAL(triggered()),
                     this, SLOT(openFile()));

    QObject::connect(ui->actionOpen_Project, SIGNAL(triggered()),
                     this, SLOT(openProject()));

    /* Connecting the edit menu */
    QObject::connect(ui->actionUndo, SIGNAL(triggered()),
                     this, SLOT(undo()));
    QObject::connect(ui->actionRedo, SIGNAL(triggered()),
                     this, SLOT(redo()));
    QObject::connect(ui->actionCut, SIGNAL(triggered()),
                     this, SLOT(cut()));
    QObject::connect(ui->actionCopy, SIGNAL(triggered()),
                     this, SLOT(copy()));
    QObject::connect(ui->actionPaste, SIGNAL(triggered()),
                     this, SLOT(paste()));
    QObject::connect(ui->actionFind_Replace, SIGNAL(triggered()),
                     this, SLOT(findReplace()));

    /* Adding the recent docs/projects menus */
    for(int i = 0; i < numRecent; i++)
    {
        recentDocsMenu.append(new QAction(tr("Recent Doc"),
                                          ui->menuRecent_Files));
        recentDocsMenu.last()
                ->setShortcut(QKeySequence(tr("CTRL+")
                                           + QString::number(i + 1)));
        QObject::connect(recentDocsMenu.last(), SIGNAL(triggered()),
                         this, SLOT(openRecentFile()));
        ui->menuRecent_Files->addAction(recentDocsMenu.last());


        recentProjectsMenu.append(new QAction(tr("Recent Project"),
                                              ui->menuRecent_Projects));
        recentProjectsMenu.last()
                ->setShortcut(QKeySequence(tr("CTRL+SHIFT+") +
                                           QString::number(i + 1)));
        QObject::connect(recentProjectsMenu.last(), SIGNAL(triggered()),
                         this, SLOT(openRecentProject()));
        ui->menuRecent_Projects->addAction(recentProjectsMenu.last());
    }
    refreshRecentMenus();
}

void EditorWindow::addTab(TabContent *doc)
{
    ui->editorTabs->addTab(doc, doc->title());

    /* Connecting to title change events */
    QObject::connect(doc, SIGNAL(titleChanged(QString)),
                     this, SLOT(tabTitleChanged(QString)));
    QObject::connect(doc, SIGNAL(lineChanged(int)),
                     this, SLOT(lineChanged(int)));

    /* Connecting to settings change events */
    doc->connectPrefs(prefs);
}


void EditorWindow::newTab()
{
    SkinDocument* doc = new SkinDocument(parseStatus, project, deviceConfig);
    addTab(doc);
    ui->editorTabs->setCurrentWidget(doc);
}

void EditorWindow::newProject()
{
    NewProjectDialog dialog(this);
    if(dialog.exec() == QDialog::Rejected)
        return;

    /* Assembling the new project if the dialog was accepted */
    NewProjectDialog::NewProjectInfo info = dialog.results();

    QDir path(info.path);
    if(!path.exists())
    {
        QMessageBox::warning(this, tr("Error Creating Project"), tr("Error:"
                             " Project directory does not exist"));
        return;
    }

    if(path.exists(info.name))
    {
        QMessageBox::warning(this, tr("Error Creating Project"), tr("Error:"
                             " Project directory already exists"));
        return;
    }

    if(!path.mkdir(info.name))
    {
        QMessageBox::warning(this, tr("Error Creating Project"), tr("Error:"
                             " Project directory not writeable"));
        return;
    }

    path.cd(info.name);

    /* Making standard directories */
    path.mkdir("fonts");
    path.mkdir("icons");
    path.mkdir("backdrops");

    /* Adding the desired wps documents */
    path.mkdir("wps");
    path.cd("wps");

    if(info.sbs)
        createFile(path.filePath(info.name + ".sbs"), tr("# SBS Document\n"));
    if(info.wps)
        createFile(path.filePath(info.name + ".wps"), tr("# WPS Document\n"));
    if(info.fms)
        createFile(path.filePath(info.name + ".fms"), tr("# FMS Document\n"));
    if(info.rsbs)
        createFile(path.filePath(info.name + ".rsbs"), tr("# RSBS Document\n"));
    if(info.rwps)
        createFile(path.filePath(info.name + ".rwps"), tr("# RWPS Document\n"));
    if(info.rfms)
        createFile(path.filePath(info.name + ".rfms"), tr("# RFMS Document\n"));

    path.mkdir(info.name);

    path.cdUp();

    /* Adding the config file */
    path.mkdir("themes");
    path.cd("themes");

    /* Generating the config file */
    QString config = tr("# Config file for ") + info.name + "\n";
    config.append("#target: " + info.target + "\n\n");
    QString wpsBase = "/.rockbox/wps/";
    if(info.sbs)
        config.append("sbs: " + wpsBase + info.name + ".sbs\n");
    if(info.wps)
        config.append("wps: " + wpsBase + info.name + ".wps\n");
    if(info.fms)
        config.append("fms: " + wpsBase + info.name + ".fms\n");
    if(info.rsbs)
        config.append("rsbs: " + wpsBase + info.name + ".rsbs\n");
    if(info.rwps)
        config.append("rwps: " + wpsBase + info.name + ".rwps\n");
    if(info.rfms)
        config.append("rfms: " + wpsBase + info.name + ".rfms\n");


    createFile(path.filePath(info.name + ".cfg"),
               config);

    /* Opening the new project */
    loadProjectFile(path.filePath(info.name + ".cfg"));
}

void EditorWindow::shiftTab(int index)
{
    TabContent* widget = dynamic_cast<TabContent*>
                         (ui->editorTabs->currentWidget());
    if(index < 0)
    {
        ui->parseTree->setModel(0);
        ui->actionSave_Document->setEnabled(false);
        ui->actionSave_Document_As->setEnabled(false);
        ui->actionClose_Document->setEnabled(false);
        ui->fromTree->setEnabled(false);
        ui->actionUndo->setEnabled(false);
        ui->actionRedo->setEnabled(false);
        ui->actionCut->setEnabled(false);
        ui->actionCopy->setEnabled(false);
        ui->actionPaste->setEnabled(false);
        ui->actionFind_Replace->setEnabled(false);
        viewer->connectSkin(0);
    }
    else if(widget->type() == TabContent::Config)
    {
        ui->actionSave_Document->setEnabled(true);
        ui->actionSave_Document_As->setEnabled(true);
        ui->actionClose_Document->setEnabled(true);
        ui->actionUndo->setEnabled(false);
        ui->actionRedo->setEnabled(false);
        ui->actionCut->setEnabled(false);
        ui->actionCopy->setEnabled(false);
        ui->actionPaste->setEnabled(false);
        ui->actionFind_Replace->setEnabled(false);
        viewer->connectSkin(0);
    }
    else if(widget->type() == TabContent::Skin)
    {
        /* Syncing the tree view and the status bar */
        SkinDocument* doc = dynamic_cast<SkinDocument*>(widget);
        ui->parseTree->setModel(doc->getModel());
        parseStatus->setText(doc->getStatus());

        ui->actionSave_Document->setEnabled(true);
        ui->actionSave_Document_As->setEnabled(true);
        ui->actionClose_Document->setEnabled(true);
        ui->fromTree->setEnabled(true);

        ui->actionUndo->setEnabled(true);
        ui->actionRedo->setEnabled(true);
        ui->actionCut->setEnabled(true);
        ui->actionCopy->setEnabled(true);
        ui->actionPaste->setEnabled(true);
        ui->actionFind_Replace->setEnabled(true);

        sizeColumns();

        /* Syncing the preview */
        viewer->connectSkin(doc);


    }

    /* Hiding all the find/replace dialogs */
    for(int i = 0; i < ui->editorTabs->count(); i++)
        if(dynamic_cast<TabContent*>(ui->editorTabs->widget(i))->type() ==
           TabContent::Skin)
            dynamic_cast<SkinDocument*>(ui->editorTabs->widget(i))->hideFind();

}

bool EditorWindow::closeTab(int index)
{
    TabContent* widget = dynamic_cast<TabContent*>
                           (ui->editorTabs->widget(index));
    if(widget->requestClose())
    {
        ui->editorTabs->removeTab(index);
        widget->deleteLater();
        return true;
    }

    return false;
}

void EditorWindow::closeCurrent()
{
    closeTab(ui->editorTabs->currentIndex());
}

void EditorWindow::closeProject()
{
    if(project)
    {
        project->deleteLater();
        project = 0;
    }

    for(int i = 0; i < ui->editorTabs->count(); i++)
    {
        TabContent* doc = dynamic_cast<TabContent*>
                          (ui->editorTabs->widget(i));
        if(doc->type() == TabContent::Skin)
        {
            dynamic_cast<SkinDocument*>(doc)->setProject(project);
            if(i == ui->editorTabs->currentIndex())
            {
                viewer->connectSkin(dynamic_cast<SkinDocument*>(doc));
            }
        }
    }

    ui->actionClose_Project->setEnabled(false);
    ui->actionExport_Project->setEnabled(false);
}

void EditorWindow::saveCurrent()
{
    if(ui->editorTabs->currentIndex() >= 0)
        dynamic_cast<TabContent*>(ui->editorTabs->currentWidget())->save();
}

void EditorWindow::saveCurrentAs()
{
    if(ui->editorTabs->currentIndex() >= 0)
        dynamic_cast<TabContent*>(ui->editorTabs->currentWidget())->saveAs();
}

void EditorWindow::exportProject()
{
    QDir dir = project->getSetting("themebase", "");
    dir.cdUp();
    QString file = project->getSetting("configfile", "").split("/").
                   last().split(".").first() + ".zip";
    file = dir.filePath(file);

    file = QFileDialog::getSaveFileName(this, tr("Export Project"),
                                        file, "Zip Files (*.zip *.ZIP);;"
                                              "All Files (*)");

    if(file != "")
    {
        ProjectExporter* exporter = new ProjectExporter(file, project, this);
        exporter->show();
    }
}

void EditorWindow::openFile()
{
    QStringList fileNames;
    QSettings settings;

    settings.beginGroup("SkinDocument");
    QString directory = settings.value("defaultDirectory", "").toString();
    fileNames = QFileDialog::getOpenFileNames(this, tr("Open Files"), directory,
                                              SkinDocument::fileFilter());

    for(int i = 0; i < fileNames.count(); i++)
    {
        if(!QFile::exists(fileNames[i]))
            continue;

        QString current = fileNames[i];

        loadTabFromSkinFile(current);

        /* And setting the new default directory */
        current.chop(current.length() - current.lastIndexOf('/') - 1);
        settings.setValue("defaultDirectory", current);

    }

    settings.endGroup();
}

void EditorWindow::openProject()
{
    QString fileName;
    QSettings settings;

    settings.beginGroup("ProjectModel");
    QString directory = settings.value("defaultDirectory", "").toString();
    fileName = QFileDialog::getOpenFileName(this, tr("Open Project"), directory,
                                            ProjectModel::fileFilter());

    settings.endGroup();
    loadProjectFile(fileName);

}

void EditorWindow::openRecentFile()
{
    loadTabFromSkinFile(dynamic_cast<QAction*>(QObject::sender())->text());
}

void EditorWindow::openRecentProject()
{
    loadProjectFile(dynamic_cast<QAction*>(QObject::sender())->text());
}

void EditorWindow::configFileChanged(QString configFile)
{

    if(QFile::exists(configFile))
    {

        if(project)
            delete project;

        project = new ProjectModel(configFile, this);
        ui->projectTree->setModel(project);

        QObject::connect(ui->projectTree, SIGNAL(activated(QModelIndex)),
                         project, SLOT(activated(QModelIndex)));

        for(int i = 0; i < ui->editorTabs->count(); i++)
        {
            TabContent* doc = dynamic_cast<TabContent*>
                              (ui->editorTabs->widget(i));
            if(doc->type() == TabContent::Skin)
            {
                dynamic_cast<SkinDocument*>(doc)->setProject(project);
                if(i == ui->editorTabs->currentIndex())
                {
                    viewer->connectSkin(dynamic_cast<SkinDocument*>(doc));
                }
            }
        }

    }


}

void EditorWindow::tabTitleChanged(QString title)
{
    TabContent* sender = dynamic_cast<TabContent*>(QObject::sender());
    ui->editorTabs->setTabText(ui->editorTabs->indexOf(sender), title);
}

void EditorWindow::showPanel()
{
    if(sender() == ui->actionFile_Panel)
        ui->projectDock->setVisible(true);
    if(sender() == ui->actionPreview_Panel)
        ui->skinPreviewDock->setVisible(true);
    if(sender() == ui->actionDisplay_Panel)
        ui->parseTreeDock->setVisible(true);
}

void EditorWindow::closeEvent(QCloseEvent* event)
{

    saveSettings();

    /* Closing all the tabs */
    for(int i = 0; i < ui->editorTabs->count(); i++)
    {
        if(!dynamic_cast<TabContent*>
           (ui->editorTabs->widget(i))->requestClose())
        {
            event->ignore();
            return;
        }
    }

    event->accept();
}

void EditorWindow::updateCurrent()
{
    if(ui->editorTabs->currentIndex() < 0)
        return;

    dynamic_cast<SkinDocument*>
            (ui->editorTabs->currentWidget())->genCode();
}

void EditorWindow::lineChanged(int line)
{
    QSettings settings;
    settings.beginGroup("EditorWindow");

    if(settings.value("autoExpandTree", false).toBool())
    {
        ui->parseTree->collapseAll();
        ParseTreeModel* model = dynamic_cast<ParseTreeModel*>
                                (ui->parseTree->model());
        parseTreeSelection = new QItemSelectionModel(model);
        expandLine(model, QModelIndex(), line,
                   settings.value("autoHighlightTree", false).toBool());
        sizeColumns();
        ui->parseTree->setSelectionModel(parseTreeSelection);
    }

    settings.endGroup();
}

void EditorWindow::undo()
{
    TabContent* doc = dynamic_cast<TabContent*>
                      (ui->editorTabs->currentWidget());
    if(doc->type() == TabContent::Skin)
        dynamic_cast<SkinDocument*>(doc)->getEditor()->undo();
}

void EditorWindow::redo()
{
    TabContent* doc = dynamic_cast<TabContent*>
                      (ui->editorTabs->currentWidget());
    if(doc->type() == TabContent::Skin)
        dynamic_cast<SkinDocument*>(doc)->getEditor()->redo();

}

void EditorWindow::cut()
{
    TabContent* doc = dynamic_cast<TabContent*>
                      (ui->editorTabs->currentWidget());
    if(doc->type() == TabContent::Skin)
        dynamic_cast<SkinDocument*>(doc)->getEditor()->cut();
}

void EditorWindow::copy()
{
    TabContent* doc = dynamic_cast<TabContent*>
                      (ui->editorTabs->currentWidget());
    if(doc->type() == TabContent::Skin)
        dynamic_cast<SkinDocument*>(doc)->getEditor()->copy();
}

void EditorWindow::paste()
{
    TabContent* doc = dynamic_cast<TabContent*>
                      (ui->editorTabs->currentWidget());
    if(doc->type() == TabContent::Skin)
        dynamic_cast<SkinDocument*>(doc)->getEditor()->paste();
}

void EditorWindow::findReplace()
{
    TabContent* doc = dynamic_cast<TabContent*>
                      (ui->editorTabs->currentWidget());
    if(doc->type() == TabContent::Skin)
        dynamic_cast<SkinDocument*>(doc)->showFind();
}


void EditorWindow::expandLine(ParseTreeModel* model, QModelIndex parent,
                              int line, bool highlight)
{
    for(int i = 0; i < model->rowCount(parent); i++)
    {
        QModelIndex dataType = model->index(i, ParseTreeModel::typeColumn,
                                            parent);
        QModelIndex dataVal = model->index(i, ParseTreeModel::valueColumn,
                                           parent);
        QModelIndex data = model->index(i, ParseTreeModel::lineColumn, parent);
        QModelIndex recurse = model->index(i, 0, parent);

        expandLine(model, recurse, line, highlight);

        if(model->data(data, Qt::DisplayRole) == line)
        {
            ui->parseTree->expand(parent);
            ui->parseTree->expand(data);
            ui->parseTree->scrollTo(parent, QAbstractItemView::PositionAtTop);

            if(highlight)
            {
                parseTreeSelection->select(data,
                                           QItemSelectionModel::Select);
                parseTreeSelection->select(dataType,
                                           QItemSelectionModel::Select);
                parseTreeSelection->select(dataVal,
                                           QItemSelectionModel::Select);
            }
        }
    }

}

void EditorWindow::sizeColumns()
{
    /* Setting the column widths */
    ui->parseTree->resizeColumnToContents(ParseTreeModel::lineColumn);
    ui->parseTree->resizeColumnToContents(ParseTreeModel::typeColumn);
    ui->parseTree->resizeColumnToContents(ParseTreeModel::valueColumn);
}

void EditorWindow::loadProjectFile(QString fileName)
{
    QSettings settings;
    settings.beginGroup("ProjectModel");

    if(QFile::exists(fileName))
    {
        projectToTop(fileName);

        if(project)
            project->deleteLater();

        ui->actionClose_Project->setEnabled(true);
        ui->actionExport_Project->setEnabled(true);

        project = new ProjectModel(fileName, this);
        ui->projectTree->setModel(project);

        /* Setting target info if necessary */
        TargetData targets;
        QString target = project->getSetting("#target", "");
        if(target != "" && targets.index(target) >= 0)
        {
            int index = targets.index(target);

            QRect screen = targets.screenSize(index);
            deviceConfig->setData("screenwidth", screen.width());
            deviceConfig->setData("screenheight", screen.height());

            if(targets.remoteDepth(index) != TargetData::None)
            {
                QRect remote = targets.remoteSize(index);
                deviceConfig->setData("remotewidth", remote.width());
                deviceConfig->setData("remoteheight", remote.height());
            }

            deviceConfig->setData("tp", targets.fm(index));
            deviceConfig->setData("Rp", targets.canRecord(index));
        }

        if(project->getSetting("#screenwidth") != "")
            deviceConfig->setData("screenwidth",
                                  project->getSetting("#screenwidth"));
        if(project->getSetting("#screenheight") != "")
            deviceConfig->setData("screenheight",
                                  project->getSetting("#screenheight"));

        QObject::connect(ui->projectTree, SIGNAL(activated(QModelIndex)),
                         project, SLOT(activated(QModelIndex)));

        fileName.chop(fileName.length() - fileName.lastIndexOf('/') - 1);
        settings.setValue("defaultDirectory", fileName);

        for(int i = 0; i < ui->editorTabs->count(); i++)
        {
            TabContent* doc = dynamic_cast<TabContent*>
                              (ui->editorTabs->widget(i));
            if(doc->type() == TabContent::Skin)
            {
                dynamic_cast<SkinDocument*>(doc)->setProject(project);
                if(i == ui->editorTabs->currentIndex())
                {
                    viewer->connectSkin(dynamic_cast<SkinDocument*>(doc));
                }
            }
        }

    }

    settings.endGroup();

}

void EditorWindow::createFile(QString filename, QString contents)
{
    QFile fout(filename);
    fout.open(QFile::WriteOnly);

    fout.write(contents.toAscii());

    fout.close();
}

void EditorWindow::docToTop(QString file)
{
    if(!QFile::exists(file))
        return;

    int index = recentDocs.indexOf(file);
    if(index == -1)
    {
        /* Bumping off the last file */
        if(recentDocs.count() >= numRecent)
            recentDocs.removeLast();
        recentDocs.prepend(file);
    }
    else
    {
        /* Shuffling file to the top of the list */
        recentDocs.removeAt(index);
        recentDocs.prepend(file);
    }

    refreshRecentMenus();
}

void EditorWindow::projectToTop(QString file)
{
    if(!QFile::exists(file))
        return;

    int index = recentProjects.indexOf(file);
    if(index == -1)
    {
        /* Bumping off the last project */
        if(recentProjects.count() >= numRecent)
            recentProjects.removeLast();
        recentProjects.prepend(file);
    }
    else
    {
        /* Shuffling file to the top of the list */
        recentProjects.removeAt(index);
        recentProjects.prepend(file);
    }

    refreshRecentMenus();
}

void EditorWindow::refreshRecentMenus()
{
    /* Clearing any deleted documents */
    for(int i = 0; i < recentDocs.count(); i++)
        if(!QFile::exists(recentDocs[i]))
            recentDocs.removeAt(i--);

    /* Clearing any deleted projects */
    for(int i = 0; i < recentProjects.count(); i++)
        if(!QFile::exists(recentProjects[i]))
            recentProjects.removeAt(i--);

    /* First hiding all the menu items */
    for(int i = 0; i < recentDocsMenu.count(); i++)
        recentDocsMenu[i]->setVisible(false);
    for(int i = 0; i < recentProjectsMenu.count(); i++)
        recentProjectsMenu[i]->setVisible(false);

    /* Then setting the text of and showing any available */
    for(int i = 0; i < recentDocs.count(); i++)
    {
        recentDocsMenu[i]->setText(recentDocs[i]);
        recentDocsMenu[i]->setVisible(true);
    }

    for(int i = 0; i < recentProjects.count(); i++)
    {
        recentProjectsMenu[i]->setText(recentProjects[i]);
        recentProjectsMenu[i]->setVisible(true);
    }
}