Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > C/C++ программирование > Qt и кроссплатформенное программирование С/С++
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 01.07.2011, 21:53   #1
xwicked
Участник клуба
 
Аватар для xwicked
 
Регистрация: 21.03.2010
Сообщений: 1,508
Лампочка Программа для создания электронных книг, C++ Qt

Здравствуйте! Вот вторая моя программа, написанная на C++(Qt). Выношу на обсуждение её и исходник - 0.0.7.

Ссылка на SourceForge.net.

Она предназначена для создания электронных книг, с централизованным хранением в базе данных SQLite3. (Подробнее будет завтра).

Этот код публикуется впервые. Его не было нигде, включая репозиторий программ Sisyphus от дистрибутива AltLinux. Там был только Клёст-кроссворд(0.1.9, 0.2.0). Комментируем Windows-версия.
Google - лучший помощник программиста.
---
моя лаборатория | мой FaceBook
xwicked вне форума Ответить с цитированием
Старый 02.07.2011, 00:03   #2
netrino
Участник клуба
 
Аватар для netrino
 
Регистрация: 15.07.2008
Сообщений: 1,933
По умолчанию

Скажу сразу, что сильно не вникал, можно сказать пробежался глазами по структуре. Не буду лукавить, исходник мне не понравился. В первую очередь следует сказать, что у Вас смешаны интерфейс с логикой, при чём сильно и даже несколько неожиданно (взять хотя бы ElectronicLibrary, в котором более 1000 строк, что не странно, ведь он реализует две не взаимосвязанные вещи: отрисовку интерфейса и работу с базой). Но ладно. Далее могу сказать про километровые методы, которым не видно конца (конкретно имею ввиду открытие книги, при чём сократить можно было как минимум в два раза, устранив дублирующийся функционал из "Открыть из файла" и "Открыть из базы"). Плюс к этому стандартный выговор за обилие публичных переменных, не соблюдение соглашения по именованию (хотя бы своего) и кошмарное форматирование (хотя это, безусловно, дело вкуса и тут упрекнуть Вас не могу). Возможно были ещё какие-то замечания, но вылетели из головы, пока рассматривал исходники.

Несмотря на всё то негативное, что сказал я выше, хочу поблагодарить за проделанную работу и более того, за открытые исходники. Успехов Вам.
netrino вне форума Ответить с цитированием
Старый 02.07.2011, 17:34   #3
xwicked
Участник клуба
 
Аватар для xwicked
 
Регистрация: 21.03.2010
Сообщений: 1,508
Лампочка

Цитата:
Сообщение от netrino Посмотреть сообщение
Скажу сразу, что сильно не вникал, можно сказать пробежался глазами по структуре. Не буду лукавить, исходник мне не понравился. В первую очередь следует сказать, что у Вас смешаны интерфейс с логикой, при чём сильно и даже несколько неожиданно (взять хотя бы ElectronicLibrary, в котором более 1000 строк, что не странно, ведь он реализует две не взаимосвязанные вещи: отрисовку интерфейса и работу с базой). ...
Согласен. Ваши слова вернули меня на два года в прошлое... Я хотел сделать сначала всё отдельно. База в файле. Вызов её, передачей параметров и считывание полученных данных со стандартного вывода. Но, со всеми этими моментами, я познакомился много позже(~1-ого года). Тогда я пошёл на беспрецедентный шаг. Сделал так, чтобы всё просто хотябы заработало - идеализация потом. Вот в таком виде этот код сейчас и представлен.
Цитата:
Сообщение от netrino Посмотреть сообщение
... Но ладно. Далее могу сказать про километровые методы, которым не видно конца (конкретно имею ввиду открытие книги, при чём сократить можно было как минимум в два раза, устранив дублирующийся функционал из "Открыть из файла" и "Открыть из базы"). ...
Задача была следующая - наглядность в понимании логики программы: одна функция делает одно завершённое действие. Так как я писал код без комментариев(они мне мешают - такое тоже бывает ) самому, чтобы не запутаться в куче методов(/функций). Про сокращение - согласен - спешил показать своему колледжу, что у меня получится...
Цитата:
Сообщение от netrino Посмотреть сообщение
... Плюс к этому стандартный выговор за обилие публичных переменных, не соблюдение соглашения по именованию (хотя бы своего) и кошмарное форматирование (хотя это, безусловно, дело вкуса и тут упрекнуть Вас не могу). Возможно были ещё какие-то замечания, но вылетели из головы, пока рассматривал исходники. ...
Переменные - так бывает, когда сразу после колледжа, следует попытка написания серьёзной программы.
Соглашение по именованию есть - этот стандарт записан в строгом соответствии с "закаулочно-канавочным мышлением"(М. Задорнов) .
Форматирование - это мне так прозрачнее видеть код.
Цитата:
Сообщение от netrino Посмотреть сообщение
...
Несмотря на всё то негативное, что сказал я выше, хочу поблагодарить за проделанную работу и более того, за открытые исходники. Успехов Вам.
Благодарю!

Добавлю: код под GNU GPL v2 - OpenSource.
Google - лучший помощник программиста.
---
моя лаборатория | мой FaceBook
xwicked вне форума Ответить с цитированием
Старый 02.07.2011, 18:33   #4
xwicked
Участник клуба
 
Аватар для xwicked
 
Регистрация: 21.03.2010
Сообщений: 1,508
Лампочка Описание

Страница книги представляет собой одну переменную QString Page.
Текст книги отображается в компоненте QTextEdit TextBook.
Текст в компоненте TextBook имеет форматирование документа HTML. Поэтому редактирование текста страницы осуществляется редактированием html-кода.

Стандартное сохранение идёт в базу данных. Но есть и возможность экспорта в файл.

Изображения хранятся, как в обычной html-странице - отдельно от текста в папке "temp". В тексте указывается путь.
При загрузке книги - во всех страницах происходит перезапись тегов изображения "<img src=":
Код:
QString str1;
int iFirst,
    iLast,
    iLength,
    iSearch;

    for(int i = 0; i < ListPages.count(); i++)
        {
            str1 = ListPages.at(i);

                if(str1.indexOf("<img src=\"") != -1)
                    {
                         int z = 1;
                         iSearch = 0;

                             while(str1.indexOf("<img src=\"", iSearch) != -1)
                                 {
                                          if(z == 1) iSearch = -1;
                                          if(iSearch == -1)
                                              {
                                                   iFirst = str1.indexOf("<img src=\"", 0);
                                                   iLast = str1.indexOf("/temp/Image", 0);
                                                   z++;
                                              }
                                          else
                                              {
                                                   iFirst = str1.indexOf("<img src=\"", iSearch);
                                                   iLast = str1.indexOf("/temp/Image", iFirst);
                                              }

                                      iFirst += 10;
                                      iLength = iLast - iFirst;
                                      str1.remove(iFirst, iLength);
                                      str1.insert(iFirst, "/tmp/xwel");
                                      ListPages.replace(i, str1);
                                      iSearch = iLast;
                                 }
                    }
        }
Это актуально для переносимой версии. В этом случае необходимо будет поменять строку:
Код:
str1.insert(iFirst, "/tmp/xwel");
на
Код:
str1.insert(iFirst, QApplication::applicationDirPath());//Текущая папка, из которой запущено приложение
Пароль книги хранится в открытом виде в переменной QString Password.
Список страниц хранится в списке строк QStringList ListPages.
Изображения хранятся в массиве QByteArray ListImages[255]. Здесь видно, что изображений на всю книгу 255.

Сохранение в базу происходит обычными SQL-запросами:
Код:
strBook = "DROP TABLE Table" + sNumBook + ";";
QueryBook.exec(strBook);
strBook = "CREATE TABLE Table" + sNumBook + " (Pages VARCHAR, Images BLOB);";
QueryBook.exec(strBook);
...
//Сохранение страниц
if(ListPages.count() != 0)
    for(int i = 0; i < ListPages.count(); i++)
        {
           QueryBook.prepare("INSERT INTO Table" + sNumBook + " (Pages) VALUES (?);");
           QueryBook.bindValue(0, ListPages.at(i));
           QueryBook.exec(/*strBook*/);
        }

//Сохранение изображений
    for(int i = 0; i < 75; i++)//Здесь видно - что сохраняются только 75 изображений(!) из 255 :(
        {
              if(ListImages[i].count() != 0)
                 {
                    QueryBook.prepare("INSERT INTO Table" + sNumBook + " (Images) VALUES (?);");
                    QueryBook.bindValue(0, ListImages[i]);
                    QueryBook.exec(/*strBook*/);
                 }
             else break;
 }
Google - лучший помощник программиста.
---
моя лаборатория | мой FaceBook

Последний раз редактировалось xwicked; 03.07.2011 в 00:56.
xwicked вне форума Ответить с цитированием
Старый 11.07.2011, 01:04   #5
xwicked
Участник клуба
 
Аватар для xwicked
 
Регистрация: 21.03.2010
Сообщений: 1,508
Лампочка Пользователям ОС Linux на RPM-основе

Для тех, кто захочет сделать свою сборку программы для размещения на собственном сайте или сервере, то вот спекфайл:
Цитата:
Summary: Qt program for creating and reading electronic books.

Summary(ru): Qt программа для создания и чтения электронных книг.

%define version 0.0.7

Group: Education

Name: klen-library

BuildRequires: gcc-c++, libqt4-sql-sqlite >= 4.4.3, libqtgui4 >= 4.4.3, libqt4-core >= 4.4.3

BuildArch: %ix86

Provides: klen-library

Release: alt1

Source: klen-library-%{version}.tar.gz

Url: http://labfreetech.org/index_e.html

Version: %{version}

License: GPLv2

Packager: LabFreeTech.org admin@labfreetech.org

%description
Qt program for creating and reading electronic books.
Supported features: - Creation of a textbook; - Editing of the textbook; - Reading the textbook; - Adding / removing books from the database; - Editing the font of the textbook; - Export and import of books in the file.

%description -l ru
Qt программа для создания и чтения электронных книг.
Поддерживаемые функции: - Создание учебника; - Редактирование учебника; - Чтение учебника; - Добавление/удаление учебников из базы данных; - Редактирование шрифта учебника; - Экспорт и импорт учебников в файл.

%prep

%setup -q

%build
%make_build
#cp -f klen-library debian/klen/opt/klen-library/

%install

cp -Rv klen/ %buildroot/

#rm -rf $RPM_BUILD_ROOT


%clean

rm -rf $RPM_BUILD_ROOT

%files

/usr/bin/*
/usr/share/*

%post

test -e /usr/bin/xwel-0.0.7 || ln -s /usr/bin/xwel-0.0.7 /usr/bin/klen-library-0.0.7
test -e $HOME/Desktop/xwel.desktop || cp -r /usr/share/applications/xwel.desktop $HOME/Desktop/klen-library.desktop

%postun

test -L /usr/bin/klen-library-0.0.7 && rm -r /usr/bin/klen-library-0.0.7
test -e $HOME/Desktop/klen-library.desktop && rm -r $HOME/Desktop/klen-library.desktop
Он находится в архиве, который специально подготовлен. Постараюсь ответить на все возникающие вопросы. Команда для сборки двоичного пакета:
Цитата:
$rpmbuild -bb /home/username/RPM/SPECS/xwel-0.0.7-alt.spec
Предварительно необходимо установить пакет rpm-build.
Google - лучший помощник программиста.
---
моя лаборатория | мой FaceBook

Последний раз редактировалось xwicked; 11.07.2011 в 17:50.
xwicked вне форума Ответить с цитированием
Старый 17.07.2011, 13:22   #6
xwicked
Участник клуба
 
Аватар для xwicked
 
Регистрация: 21.03.2010
Сообщений: 1,508
Лампочка Клён-библиотека 0.0.8

Здравствуйте!

Следующая версия программы 0.0.8, 0.0.8-rpm. Её ключевой особенностью является импорт html-файла. Вот функция:
Код:
void ElectronicLibrary::OpenHTMLFile()
{
     QString FileNameBook, str, sHTML;
     FileNameBook = QFileDialog::getOpenFileName(0, tr("Book import"), "", "*.html");

         if(FileNameBook == "") return;
         else
             {
                 QFile ifile(FileNameBook);
                 QTextStream istream(&ifile);
                 float i = 0;//Величина %
                 float ipt,//Численное значние "pt"
               iptc;//Отвечает за таблицу
                 iptc = 0;
                 ipt = 0;
                 ifile.open(QIODevice::ReadOnly);

                     while(!ifile.atEnd())
                         {
                             str.append(istream.readLine());
                             str.append(" ");
                             sHTML.append(str);

                                 if(str.indexOf("<TABLE") != -1) iptc = 1;//Начало таблицы
                                 if(str.indexOf("/TABLE>") != -1) iptc = 2;//Конец таблицы

                             int j, iptc2;
                             QString s1,s2;
                             s1.clear();
                             s2.clear();
                             iptc2 = 0;

                //Поиск элемента обозначения шрифта
                             j = str.indexOf("font-size:");

//Если элемент найден и найдено вхождение "pt", то
                                 if(j != -1 && str.indexOf("pt", j) != -1)
                                     {
                      //Добавление одной цифры, стоящей за "pt"
                                          s1.append(str.at(str.indexOf("pt", j) - 1));

//Если второй символ стоящий за первым числом не пробел, то
                                              if(str.at(str.indexOf("pt", j) - 2) != ' ')
                      //Добавить его как вторую цифру числа шрифта(ex. "14pt")
                          s2.append(str.at(str.indexOf("pt", j) - 2));

              //Соединить с первой цифрой в s1. Образовать число.
                                              if(s2 != "") s1.append(s2);

                                          bool bOk;
                                          ipt = s1.toFloat(&bOk);
//6pt = 113 = 0.885% от страницы
//16pt = 42 = 2.380% от страницы
//0.1375 - условный средний коэффициент % занимаемого значения 6pt на одной странице, формата A4
//Усреднённая формула нахождения % текущей строки от страницы, формата A4, относительно 6pt
                                          ipt = (ipt / 6) * 0.1375;
                                     }

//Поиск элемента обозначения шрифта другой формы
                                 if(j == -1)
                                     {
                                         if(str.indexOf("FONT SIZE=1") != -1) j = 1 ;
                                         if(str.indexOf("FONT SIZE=2") != -1) j = 2 ;
                                         if(str.indexOf("FONT SIZE=3") != -1) j = 3 ;
                                         if(str.indexOf("FONT SIZE=4") != -1) j = 4 ;
                                         if(str.indexOf("FONT SIZE=5") != -1) j = 5 ;
                                         if(str.indexOf("FONT SIZE=6") != -1) j = 6 ;
                                         if(str.indexOf("FONT SIZE=7") != -1) j = 7 ;

                                             switch(j)//Вычисление процента
                                                 {
                                                      case 1:
                                                          ipt = (8 / 6) * 0.1375;
                                                      case 2:
                                                          ipt = (10 / 6) * 0.1375;
                                                      case 3:
                                                          ipt = (12 / 6) * 0.1375;
                                                      case 4:
                                                          ipt = (14 / 6) * 0.1375;
                                                      case 5:
                                                          ipt = (18 / 6) * 0.1375;
                                                      case 6:
                                                          ipt = (24 / 6) * 0.1375;
                                                      case 7:
                                                          ipt = (36 / 6) * 0.1375;
                                                 }
                                     }
...
Google - лучший помощник программиста.
---
моя лаборатория | мой FaceBook
xwicked вне форума Ответить с цитированием
Старый 17.07.2011, 13:22   #7
xwicked
Участник клуба
 
Аватар для xwicked
 
Регистрация: 21.03.2010
Сообщений: 1,508
Лампочка Клён-библиотека 0.0.8 (продолжение)

Код:
...
                             i += ipt;//Суммирование процента

                                 if(i >= 100)//Если набралось 100%
                                     {
                                         if(iptc == 0)//Если нет таблицы в конце страницы
                                             {
                                                  sHTML.append("</body></html>");
                                                  ListPages << sHTML;
                                                  sHTML.clear();
                                                  sHTML.append("<html><body>");
                                                  i = 0;
                                             }
                                         else
                                             {
        //Если таблица была закрыта на данной странице
                                                  if(iptc == 2)
                                                      {
                                                           sHTML.append("</body></html>");
                                                           ListPages << sHTML;
                                                           sHTML.clear();
                                                           sHTML.append("<html><body>");
                                                           i = 0;
                                                           iptc = 0;//Значение = таблиц нет
                                                      }
                                             }
                                     }

                             str.clear();
                         }

                     if(sHTML != "") ListPages << sHTML << "</body></html>";

                 ifile.close();

                     if(gRegimeViewing == true) return;
}
Она производит разбивку на страницы, подсчитывая условное значение количества строк располагаемых на странице, формата A4. Была ещё задумка подсчитать количество символов в строке но, пока решил остановиться на таком варианте.

Разбивая страницы, она переносит всю таблицу на одну страницу до конца, даже, если таблица физически должна быть на 2-х или более страницах.

Импортируются пока только html-страницы, созданные в OpenOffice. Это делалось для того, чтобы преподаватели, у которых имеются методички в формате MSWord(*.doc) могли наиболее быстро перегнать своё пособие в мою программу.

Как Вам эта функция и есть ли возможность всё это максимально упростить? Благодарю за ответ.

P.S. Следующим сообщением я выложу архив для сборки deb-пакетов, дистрибутивов ОС Linux Debian, Ubuntu.
Google - лучший помощник программиста.
---
моя лаборатория | мой FaceBook
xwicked вне форума Ответить с цитированием
Старый 24.07.2011, 20:30   #8
xwicked
Участник клуба
 
Аватар для xwicked
 
Регистрация: 21.03.2010
Сообщений: 1,508
Лампочка Клён-библиотека 0.0.9

Теперь и счастливым обладателям дистрибутивов GNU / Linux Debian и его производным(Ubuntu), предоставилась возможность попробовать себя в качестве разработчиков ПО.

Новая версия электронной библиотеки 0.0.9, 0.0.9-rpm, 0.0.9-deb.

Серьёзным изменением в данной версии является ускорение загрузки книги из базы данных. Это связано с выгрузкой изображений из базы на диск в папку. Сначала я не знал, как сделать лучше, поэтому сделал выгрузку по одному байту, что занимало большой промежуток времени.

При использовании объекта QImage - этот процесс уcкорился на моей тестовой машине раз в 20(!). Код ниже:
Код:
              for(int i = 0; i < 75; i++)
                  if(ListImages[i].count() != 0)
                      {
                          QString sNumImage;
                          QByteArray sByteImage;
                          sByteImage.clear();
                          sNumImage.setNum(i);

                              if(i >= 0 && i <= 9) sNumImage.insert(0,"0");
                                  ifile.setFileName("/tmp/xwel/temp/Image" + sNumImage + ".png");

                          QDataStream sifile(&ifile);
                          uchar cdata;
                          ifile.open(QIODevice::WriteOnly);
                          sByteImage.append(ListImages[i]);

                              while(sByteImage.count() != 0)
                                  {
                                      cdata = uchar(sByteImage.at(0));
                                      sifile << cdata;
                                      sByteImage.remove(0, 1);
                                  }

                              if(i > SelectWordDialog->value()) SelectWordDialog->setValue(i);

                          QApplication::processEvents();

                              if(SelectWordDialog->wasCanceled()) break;
                      }
Код:
            for(int i = 0; i < CountImages; i++)
                if(ListImages[i].count() != 0)
                    {
                        QString sNumImage;
                        QByteArray sByteImage;
                        sByteImage.clear();
                        sNumImage.setNum(i);

                            if(i >= 0 && i <= 9) sNumImage.insert(0,"0");

                        sByteImage.append(ListImages[i]);
                        QImage ImageSave;
                        ImageSave.loadFromData(sByteImage);
                        ImageSave.save("/tmp/xwel/temp/Image" + sNumImage, "PNG");

                            if(i > SelectWordDialog->value()) SelectWordDialog->setValue(i);

                        QApplication::processEvents();

                            if(SelectWordDialog->wasCanceled()) break;
                    }
Для сборки deb-пакета необходимо наличие установленных пакетов: autotools-dev, dh-make, fakeroot и другие.
Команда сборки:
Цитата:
user@pc:/klen-library-0.0.9-deb$ dpkg-buildpackage -rfakeroot
ЗЫ: Возможно скоро напишу краткое руководство по сборке deb-пакетов и выложу на своём сайте. Это будет частичный перевод данного руководства начинающего мейнтейнера.
Google - лучший помощник программиста.
---
моя лаборатория | мой FaceBook

Последний раз редактировалось xwicked; 24.07.2011 в 20:33.
xwicked вне форума Ответить с цитированием
Старый 29.07.2011, 21:28   #9
xwicked
Участник клуба
 
Аватар для xwicked
 
Регистрация: 21.03.2010
Сообщений: 1,508
Лампочка Клён-библиотека 0.1.0

Здравствуйте!

В этой версии я сделал одно важное изменение, на которое хотел бы обратить Ваше особое внимание. Это функция "int slotChangeFont()". Там я копирую в переменную QString sText всё выделение:
Код:
sText = TextBook->textCursor().selection().toHtml();
Потом заменяю все вхождения модификаторов шрифта: "font-family:", "font-size:", "font-style:", "font-weight:" на значения из диалога шрифта.

Так вот, там должен быть способ в одну единственную строчку. Я даже нашёл его - записал в свою амбарную тетрадь, а сейчас найти не могу . Прошу помочь в нахождении этого способа, так как "int slotChangeFont()" занимает не много не мало, аж 195 строк(!). Буду благодарен.

Исходный код: 0.1.0, 0.1.0-rpm, 0.1.0-deb.

Что изменилось:
Цитата:
- Добавлены значки "Oxygen", распространяемые по лицензии GNU GPL;
- При редактировании шрифта, он изменяется теперь, не для всего текста страницы, а по частям(словам, предложениям, абзацами и т. д.);
- Улучшен импорт HTML-документов, включая копирование изображений, которые преобразуются в формат PNG;
- Изменена стандартная панель - добавлены несколько новых действий;
- Добавлена панель редактирования, в которой продублированы действия из меню "Редактирование", для более удобной работы.
- Счётчик страниц переместился со стандартной панели в строку состояния.
Google - лучший помощник программиста.
---
моя лаборатория | мой FaceBook

Последний раз редактировалось xwicked; 29.07.2011 в 21:37.
xwicked вне форума Ответить с цитированием
Старый 10.08.2011, 18:36   #10
xwicked
Участник клуба
 
Аватар для xwicked
 
Регистрация: 21.03.2010
Сообщений: 1,508
Лампочка Клён-библиотека 0.1.1

Здравствуйте! Вот новая версия 0.1.1 - исходный код + сборки.

Внешний вид:


Одно из добавлений - это функция поиска текста по книге:
Код:
           while(iNumberPage < ListPages.count())
               {
                 lFind:
                    if(TextBook->find(sFindText) == false)
                        {
                            slotNextPage(); //iNumberPage++

                                if(iNumberPage == ListPages.count() - 1) break;

                            goto lFind;
                        }
                    else break;
               }
QString sFindText - искомый текст;
int iNumberPage - текущая страница
QStringList ListPages - список страниц с текстом.

Для поиска используется "TextBook->find(sFindText)" - будет ли выгода, если использовать функцию поиска в QString непосредственно или использовать контейнер для ускорения? Предполагаю, что сейчас тратится дополнительное время для прорисовки QTextEdit TextBook.

Все новинки:
Цитата:
- Добавлена возможность импорта / экспорта простого кодированного текста в формате utf-8;
- Добавлена функция поиска слов по книге;
- Добавлена возможность расширенного редактирования вставленного простого текста;
- Исправлена ошибка, при ответе "Отмена", вопроса о сохранении, при выходе;
- Добавлена панель поиска, с дубликатами команд из меню "Поиск";
- Исправлено несколько ошибок с кнопками панелей;
- Исправлены несколько ошибок при импорте файлов.
Google - лучший помощник программиста.
---
моя лаборатория | мой FaceBook

Последний раз редактировалось xwicked; 10.08.2011 в 18:52. Причина: Добавление фото :)
xwicked вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Открытие электронных книг referent Общие вопросы Delphi 1 07.04.2012 16:05
читалка электронных книг для чтения pdf Arassir Компьютерное железо 1 15.11.2011 12:56
программа для рассылки электронных писем sergey6262 Общие вопросы .NET 1 19.09.2010 23:43
программа для рассылки электронных писем sergey6262 WordPress и другие CMS 2 17.09.2010 00:31
программа для рассылки электронных писем sergey6262 Общие вопросы Delphi 1 14.09.2010 22:09