Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: просмотр больших файлов  (Прочитано 11367 раз)
0 Пользователей и 1 Гость смотрят эту тему.
lex452
Интересующийся

ru
Offline Offline

« : 10-02-2011 16:11 » 

Есть код программы которая открывает большие файлы и позволяет динамически подгружать из них данные при перелистывании

Код:
using System;
using System.Drawing;
using System.Windows.Forms;
using System.IO;
using System.Text;

namespace WindowsFormsApplication75
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            OpenFileDialog ofd = new OpenFileDialog();
            if (ofd.ShowDialog(this) == System.Windows.Forms.DialogResult.OK)
            {
                FileStream stream = new FileStream(ofd.FileName, FileMode.Open, FileAccess.Read);
                FileViewer viewer = new FileViewer(stream);
                viewer.Parent = this;
                viewer.Dock = DockStyle.Fill;
            }
        }
    }

    //Просмотрщик больших файлов
    public class FileViewer : UserControl
    {
        Stream stream;
        int CharHeight;
        int CharWidth;

        public FileViewer(Stream stream)
        {
            this.stream = stream;
            //
            Font = new Font(FontFamily.GenericMonospace, 10);
            CharHeight = 15;
            CharWidth = 9;
            //
            AutoScroll = true;
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            stream.Dispose();
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);
            if (ClientRectangle.Width > 0)
            {
                int symbolsPerPageLine = ClientRectangle.Width / CharWidth;
                int linesPerPage = ClientRectangle.Height / CharHeight;
                int linesCount = (int)stream.Length / symbolsPerPageLine;
                AutoScrollMinSize = new Size(1, 1 + linesCount + ClientRectangle.Height - linesPerPage);
                VerticalScroll.SmallChange = 1;
                VerticalScroll.LargeChange = ClientRectangle.Height / CharHeight;
                Invalidate();
            }
        }

        protected override void OnScroll(ScrollEventArgs se)
        {
            base.OnScroll(se);
            Invalidate();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            //
            int symbolsPerLine = ClientRectangle.Width / CharWidth;
            int linesPerPage = ClientRectangle.Height / CharHeight;
            int startPos = VerticalScroll.Value * symbolsPerLine;
            stream.Seek(startPos, SeekOrigin.Begin);
            //
            byte[] buffer = new byte[symbolsPerLine];
            //
            for(int y=0; y < linesPerPage; y++)
            if (stream.Position < stream.Length)
            {
                int count = stream.Read(buffer, 0, buffer.Length);
                char[] chars = Encoding.Default.GetChars(buffer, 0, count);
                for (int x = 0; x < chars.Length; x++)
                    e.Graphics.DrawString(chars[x].ToString(), Font, Brushes.Black, x * CharWidth, y * CharHeight);
            }
        }
    }
}
подскажите как переделать ее так чтобы файл можно было просматривать таким же образом, но через richTexBox?Не понял
« Последнее редактирование: 10-02-2011 19:42 от Джон » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 10-02-2011 16:53 » 

lex452, унаследовать от RichTextBox, сделать моноширинный шрифт, и вместо рисования на экране записывать текст в соответствующие свойства RichTextBox. Естественно, скроллинг либо потеряется, либо придётся загружать весь файл целиком, либо реализовывать скроллинг отдельно.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
lex452
Интересующийся

ru
Offline Offline

« Ответ #2 : 10-02-2011 17:50 » 

в том то и дело, что целый файл не получится никак загрузить, и времени много займет и тупо он не поместиться в richTextBox
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 10-02-2011 17:59 » 

lex452, и?
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
lex452
Интересующийся

ru
Offline Offline

« Ответ #4 : 10-02-2011 18:06 » 

весь функционал значит не получится перенести в textBox???
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 10-02-2011 18:07 » 

lex452, интересно и содержательно поговорили. Ну, успехов...
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
lex452
Интересующийся

ru
Offline Offline

« Ответ #6 : 10-02-2011 18:10 » 

я пробовал и не смог сделать чтобы при скролинге файл подгружался динамически, может есть какие то идеи?  Как определить скролинг в какую сторону идет вверх или низ???
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #7 : 10-02-2011 20:28 » 

lex452, какой "весь функционал"? О чём вообще речь? Что значит, как определить, куда идёт скроллинг, если скроллинг у RichTextBox относится только к содержимому этого text box, которое является лишь частью файла - это вообще неадекватный скроллинг, его нельзя использовать, потому что scroll bar должен знать про весь файл, а не про его часть.

Ты слышал слова "постановка задачи", "техническое задание" и т.п.? Где всё это? Какие пользовательские функции должны присутствовать в конечном решении, которое ты хочешь получить? Какое отношение к этим пользовательским функциям имеет RichTextBox? Почему мне или другим читателям об этом надо догадываться? Телепатов тут нет, RichTextBox не умеет работать с файлами по частям - это в первую очередь редактор и устроен он именно для этого, а приведённое тобою решение вовсе не является редактором, это просто отрисовка куска текстового файла, и устроена она соответственно.

Если использовать гротеск, то твоя проблема выглядит, как нижеследующая:

Есть код:
Код: (ASM)
mov ah, 08h
Как мне его переделать, чтобы получилась операционная система?

Ответ: никак, потому что строчка ассемблерного кода - это про Фому, а операционная система - это про Ерёму.

P.S.
Цитата: lex452
я пробовал и не смог сделать
Если пробовал, то показывай, что и как пробовал.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
lex452
Интересующийся

ru
Offline Offline

« Ответ #8 : 10-02-2011 21:47 » 

Нужен просмотр больших файлов, (с разными кодировками и все такое), типа как в FarManager ну или Lister в тоталкоммандере. Для отображения файла хочу использовать richTextBox (не вижу лучшего компонента для этой цели), но некоторые файлы по просту не помещаются в richTextBox или их открытие занимает слишком много времени. Я думал реализовать это загрузкой в richTextBox файла не полностью, а частями способными поместиться на экран и при скролинге удалять первые строки и добавлять в конец новые, пропорционально скролингу. а вот как реализовать это не знаю, потому что даже не нашел как определить в какую сторону прокручивается скроллбар вверх или вниз. Подскажите может есть другие идеи как это сделать?Не понял?
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #9 : 11-02-2011 06:02 » 

Цитата: lex452
Нужен просмотр больших файлов, (с разными кодировками и все такое), типа как в FarManager ну или Lister в тоталкоммандере. Для отображения файла хочу использовать richTextBox (не вижу лучшего компонента для этой цели)
Я по приведённому коду вижу, что уже имеющееся у тебя решение работает в общих чертах как Lister в TotalCommander. Поэтому утверждение "нет лучшего компонента для этой цели" - либо ерунда, либо цель указана неправильно. Я лично никакой необходимости в RichTextBox пока не вижу.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #10 : 11-02-2011 07:48 » 

Я с RichText пробовал работать только в WinAPI и было это лет 10 назад - говорю о том, как было состояние дел в win 9x, но не думаю, что кардинально что-то изменилось.  Подгрузка текста на ходу там не предусмотрена - только заливка разом всего буфера. Т.ч. считаю, построчная смена (для имитации скролирования для больших объемов) будет проблемой.
Коли есть уже реализованный компонент, который рендерит текст и делает это так, как надо, то нет смысла использовать RichTextBox.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.

"железокаменный метеорит" мог образоваться от расплавления металлических конструкций в результате например ядерного взрыва и стекания жидкого железа в какой нибудь щебень (c) Иванов С.
lex452
Интересующийся

ru
Offline Offline

« Ответ #11 : 11-02-2011 10:17 » 

В этом компоненте я не понял как сделать перенос строк чтоб не сплошным текстом печаталось
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #12 : 11-02-2011 12:49 » 

lex452, вручную сделать. Количество строк и столбцов известно - каждую строку из буфера при выводе на экран переносить на другую строку столько раз, сколько нужно. При этом запоминать количество добавленных строк и учитывать при прокрутке и при выполнении seek файлового потока.

RXL, на самом деле рендером RichTextBox можно воспользоваться, если завести Bitmap, а при OnPaint сначала загружать текст в невидимый RichTextBox, и переопределить его OnPaint таким образом, чтобы Graphics он брал не с экрана, а из Bitmap. Аналог собственной двойной буфферизации. Но правильно подключить неразмещённый на форме RichTextBox к событиям формы - это задача, требущая аккуратности. ScrollBar при этом могут быть независимыми элементами управления, а не свойством UserControl.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
lex452
Интересующийся

ru
Offline Offline

« Ответ #13 : 11-02-2011 18:09 » 

А кто сказал что на форме нет richTextBox??? он там есть поподробнее опишите как это сделать???
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #14 : 11-02-2011 20:25 » 

Цитата: lex452
А кто сказал что на форме нет richTextBox???
Я сказал.

Цитата: lex452
поподробнее опишите как это сделать???
Ты предлагаешь мне написать для тебя готовый компонент? Идею я изложил, а копаться в MSDN и писать код я не собираюсь - мне это неинтересно.

Интересными мне бывают сложные для меня проблемы, которые я не знаю, как решать. Интересным бывает научить человека, который хочет учиться. Ты желания учиться не показываешь. Пока ты показал, судя по всем признакам, кусок чужого кода, в котором ты не понимаешь отдельных строчек, не показал ни одной строчки своего, пожаловался на судьбу в духе "не получается" и высказал пожелания в духе "разжевать и в рот положить".

Так дела не делаются.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
lex452
Интересующийся

ru
Offline Offline

« Ответ #15 : 12-02-2011 14:06 » 

так дело в том что  я не знаю даже с чего начать, потому что не смог найти некоторых свойств которые мне необходимы. Например для отслеживания скрола  в richTextBox. А желание научиться у меня есть как раз, код свой не могу выложить потому что пишу прогу на работе, а от туда скинуть ничего нельзя(((
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #16 : 12-02-2011 15:03 » 

lex452, для начала расскажи о себе и об этой задаче. Что это за программу ты пишешь, для решения каких задач пишется эта программа, почему ты выбрал те инструменты, которые выбрал? Каков твой уровень профессиональной программистской подготовки: студент профильной или непрофильной специальности, специалист непрофильной специальности, которому по работе пришлось столкнуться с необходимостью программировать (если да, то какова основная специальность), любитель?

Эта информация позволит наладить общение, поскольку я сейчас даже не знаю, на каком языке с каким уровнем доходчивости с тобой разговаривать. Кроме того, важно знать, чего ты хочешь получить в результате - это позволит правильно расставить акценты в разговорах. Например, непрофильный специалист, которому надо побыстрее решить одноразовую задачу - это одно, студент, которому надо научиться решать любые подобные задачи - это совсем другое.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
lex452
Интересующийся

ru
Offline Offline

« Ответ #17 : 13-02-2011 20:08 » 

Я не студент, но по работе столкнулся с необходимостью учиться программировать, мой уровень это можно сказать начальный. В приложении я загружаю список файлов в listBox
и необходимо реализовать функцию просмотра файла (аналогично Far (F3)), чтобы пользователям привыкшим работать в Far, не нужно было привыкать к работе с новым приложением. При просмотре необходима возможность смены кодировки (Win и Dos), желательно переключаться между файлами по нажатию (+ -).
Решил использовать richTextBox, потому что как я понял он практически для этого и предназначен, но столкнулся с проблемой, что большие файлы не могут полностью поместиться в компоненте (возникает ошибка OutOfMemory), и загрузка всего файла занимает много времени, что создает неудобство при работе. На форумах подсказали что в Far файлы загружаются не полностью, а подгружаются в зависимости от скрола. Я попытался это реализовать но не смог найти свойство отвечающее за позицию скролбара и не понял как определить в какую сторону перемещается скроллбар, поэтому получилось что даже начать не с чего. Вот, если есть какие нибудь идеи буду очень благодарен.


Добавлено через 1 минуту и 50 секунд:
И для меня это больше не одноразовая задача, просто я ее сам перед собой поставил и главная цель это научиться.
« Последнее редактирование: 13-02-2011 20:10 от lex452 » Записан
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #18 : 13-02-2011 21:29 » 

Я бы использовал такой подход:

1. Разместил на форме элемент для просмотра текста (кстати, если текст не содержит сложного форматирования с разными шрифтами, раскраской и т.д., то вполне можно обойтись обычным textBox) с выключенным вертикальным scrollbar'ом.

2. Прилепил отдельно справа вертикальный скроллбар.

3. Смастерил клас для работы с текстовым файлом, который кэширует ранее просмотренный текст (для скроллинга назад без задержек) и подчитывает в кэш еще не просмотренный текст (для скроллинга вперед без задержек).

4. Заносил в textBox ровно столько строк, сколько умещается в нем.

5. При скроллинге вниз удалял самую верхнюю строку textBox и добавлял вниз очередную строку из кэша.

6. При скроллинге вверх удалял самую нижнюю строку textBox и добавлял вверх очередную строку из кэша.

В пп. 5 и 6 нужно предусмотреть синхронизацию содержимого текстового окошка с положением scrollbar.

Добавлено через 3 минуты и 35 секунд:
Для синхронизации textBox и scrollbar удобно воспользоваться паттерном GoF Mediator.
« Последнее редактирование: 13-02-2011 21:33 от Dale » Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
lex452
Интересующийся

ru
Offline Offline

« Ответ #19 : 14-02-2011 04:31 » 

Я приблизительно mfr и хочу сделать, только вот какие свойства использовать чтобы отследить куда происходит скрол вверх или низ и как сделать скрол изменяемым по сравнению с размером текста, скрол же всегда одинаковый
Записан
Вад
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #20 : 14-02-2011 05:53 » 

lex452, взять размер файла и просто засунуть его в качестве размера скролла (можно разделить на примерную ширину одной строки, особенно если шрифт моноширинный, чтобы "плашка" скролла была более разумная).

При скроллировании рассчитывать позицию в файле, соответствующую данному положению скролла и сделать поправку на начало соответствующей строки (опять же, при моноширинном шрифте или отформатированном файле - попроще)

То есть, в самом простом случае: имеем строку в 80 символов, открываем файл на 80000 символов и получаем 1000 строк. Пусть на экран помещается 25 строк: тогда скролл будет принимать значения 0..(999-25), или 0..974. (Ну, или 0..999, если не париться о том, что проскроллится до места, где последняя строка будет наверху, а за ней - пустота). Теперь тыкаемся в скролл, попадаем, допустим, на значение скролла 250 (куда-то в 1/4 всей полосы): 250*80 = 20000. Идём и рисуем в textBox, начиная с 20000-ного символа в файле.
« Последнее редактирование: 14-02-2011 06:01 от Вад » Записан
lex452
Интересующийся

ru
Offline Offline

« Ответ #21 : 14-02-2011 10:47 » 

Все равно не понял, как отследить положение скрола??? какое свойство?? и как понять куда он листает вверх или низ??
Записан
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #22 : 14-02-2011 11:26 » 

lex452, скролбар - это GUI элемент, который генерит события пользовательского ввода. См. его описание в MSDN.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.

"железокаменный метеорит" мог образоваться от расплавления металлических конструкций в результате например ядерного взрыва и стекания жидкого железа в какой нибудь щебень (c) Иванов С.
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #23 : 14-02-2011 11:50 » 

Все равно не понял, как отследить положение скрола??? какое свойство??

Нужно отслеживать текущую позицию в файле и синхронизировать с ней:
1. textBox;
2. scrollbar;
3. текстовый кэш.

и как понять куда он листает вверх или низ??

Сама по себе программа просмотра ничего не листает. Измениться текущая позиция в файле может в результате:
1. нажатия клавиш управления курсором (вверх/вниз на строчку);
2. нажатия клавиш PadeUp/Down (вверх/вниз на экран);
3. протяжки скроллбара мышью;
4. поиска текста по образцу.

Интерфейсные элементы должны синхронизироваться с событием изменения текущей позиции (см. предыдущий пункт).
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
lex452
Интересующийся

ru
Offline Offline

« Ответ #24 : 14-02-2011 15:05 » 

Подскажите какре событие возникает при прокручивании колесиком мыши?Не понял И как определять вверх или низ???

Добавлено через 17 минут и 33 секунды:
И я вот еще о чем думал, как отследить какую часть текста считал в прошлый раз?? Постоянно осуществлять поиск по тексту??? А вдруг в нем будут одинаковые строки???

Добавлено через 15 минут и 17 секунд:
И еще как понять что курсор дошел до конца текста, т.е. то что он на нижней строке textBoxa??

Добавлено через 13 минут и 37 секунд:
 и ничего не нашел про gof mediator? где про это почитать??
« Последнее редактирование: 14-02-2011 15:22 от lex452 » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #25 : 14-02-2011 16:25 » 

Цитата: lex452
Подскажите какре событие возникает при прокручивании колесиком мыши?
MouseWheel. Подробности в MSDN.

Цитата: lex452
И я вот еще о чем думал, как отследить какую часть текста считал в прошлый раз?? Постоянно осуществлять поиск по тексту??? А вдруг в нем будут одинаковые строки???
Текст тут ни при чём. Чтение определяется позицией в файле - это число, индекс байта. Любой кусок определяется позицией начала и конца. Соотнести позицию с интервалом или два числовых интервала между собой и определить, что делать дальше - задача тривиальная. Нужно только продумать как следует, нарисовать на листочке карандашом, что и как меняется.

Цитата: lex452
И еще как понять что курсор дошел до конца текста, т.е. то что он на нижней строке textBoxa??
Зачем курсор для просмотра?

Цитата: lex452
и ничего не нашел про gof mediator? где про это почитать??
Вот это очень странно. По строчке gof mediator Гугл и Яндекс выдают вагон ссылок. Из них (даже на русском языке) не составляет труда выяснить, что GoF - это условное обозначение так называемой "банды четырёх", а собственно шаблон называется mediator.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
lex452
Интересующийся

ru
Offline Offline

« Ответ #26 : 15-02-2011 12:15 » 

Текст тут ни при чём. Чтение определяется позицией в файле - это число, индекс байта. Любой кусок определяется позицией начала и конца. Соотнести позицию с интервалом или два числовых интервала между собой и определить, что делать дальше - задача тривиальная. Нужно только продумать как следует, нарисовать на листочке карандашом, что и как меняется.

Каким способом ты предлагаешь считывать файл, и где там будет этот байт начала и конца, и интервалы??
Записан
Вад
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #27 : 15-02-2011 12:25 » 

У FileStream есть не только метод Read, позволяющий читать нужное количество байт начиная с текущей позиции (или смещения относительно неё), но также метод Seek (переход на нужную позицию в файле) и свойство Length, указывающее общий размер файла. Детали, как всегда, в MSDN.
« Последнее редактирование: 15-02-2011 12:27 от Вад » Записан
lex452
Интересующийся

ru
Offline Offline

« Ответ #28 : 15-02-2011 12:48 » 

Я работал с FileStream ? только вот считывал обычно по строчно, кстати а в FileStream нельзя задать количество считываемых строк и позицию строки в файле??

Добавлено через 1 день, 4 часа, 58 минут и 16 секунд:
Получилось считывать файл с буфером равным размеру видимой части textBox1 через FileStream, но только если текст будет заполнять textBox1 полностью, а строки на самом деле переносятся и их получается больше чем может поместиться в видимой части textBox1. Может можно как-то считывать по строкам и запоминать на какой строке остановилось предидущее считывание??
Еще столкнулся с проблемой что при преобразовании byte в char не отображается русская буква 'Ф', в чем может быть проблема??
« Последнее редактирование: 16-02-2011 17:47 от lex452 » Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines