Tutorials‎ > ‎

Qt Quick 2.0 Application - QML and C++ Integration

posted Nov 25, 2013, 2:46 AM by Muhammad Zullidar   [ updated Aug 15, 2016, 11:34 PM by Surya Wang ]

In this tutorial, i will show you how to use C++ object as a model and display the data using QML. For your information, i made this application on Windows 7 x86 Pro, using Qt SDK 5.0.0, Qt Creator 2.6.1, and Qt Quick 2.0. I assummed that you had already know about C++, QML and Qt's basic.
The first thing that we have to do is make a Qt Quick Application project:
  1. Create new Qt Quick 2.0 Application project. To create new project, we open File -> New File Or Project
  2. Choose Qt Quick 2.0 Application, and then click Next button.
  3. Type your project name, and then choose the location of the project.
  4. Because we want to make a desktop application on windows 32 bit and using MSVC2010 as its compiler, then choose Desktop Qt 5.0.0 MSVC2010 32bit. Leave the debug and release checkbox checked.
  5. In this window, we can see the project summary that we have created. Click Finish button.

We have created our project than we can start making our application.

This application is an application that show blitzmegaplex's movie schedule. We get the data from the RSS provided by blitzmegaplex. You can get the movie schedule rss using this link : http://www.blitzmegaplex.com/rss/schedule.php.
So we get the data using QNetworkAccessManager. This class allows your application retrieving data through network. After the class finished retrieving data, we use QXmlStreamReader to read the XML data. We create two model classes that can be exposed to QML. This two classes subclass QObject class, so its property and function can be accessed by using Q_PROPERTY macro and Q_INVOKABLE macro. To expose that class model to QML, we using QQmlContext. This class make our class can be accessed in QML globally by using QQmlContext::setContextProperty(). But before we use that function, we have to initialize QQmlContext object with QQuickView::rootContext().

Starting create the application

We make two model classes, MovieScheduleModel and MovieScheduleDetail. These classes will contain information about the movie schedule we retrieved from through network. We also make MovieScheduleData class to handle data collection from network and get details of movie schedule.
To make new class, here is the example :
  1. Right click on your project, and choose Add New...
  2. Choose C++ Class, and then click Next.
  3. Enter the class name and choose its superclass. We choose the superclass for all of our classes to QObject. and then click Next.
  4. We can see the summary of our class and then choose the project that we want to be added with the class. We choose TestingQt.pro project file. And then click Finish.
Do the step above to create all of our new C++ classes.

Start writing C++ class code

First, We start writing movieschedulemodel.h. This header file contains class definition. Here is the code.
#ifndef MOVIESCHEDULEMODEL_H
#define MOVIESCHEDULEMODEL_H

#include <QObject>
#include <QStringList>

class MovieScheduleModel : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString id READ id WRITE setId NOTIFY idChanged)
    Q_PROPERTY(QString theater READ theater WRITE setTheater NOTIFY theaterChanged)
    Q_PROPERTY(QString link READ link WRITE setLink NOTIFY linkChanged)
    Q_PROPERTY(QStringList titles READ titles WRITE setTitles NOTIFY titlesChanged)
    Q_PROPERTY(QStringList times READ times WRITE setTimes NOTIFY timesChanged)
public:
    explicit MovieScheduleModel(QObject *parent = 0);
    QString id() const;
    void setId(const QString &newid);
    QString theater() const;
    void setTheater(const QString &newtheater);
    QString link() const;
    void setLink(const QString &newlink);
    QStringList titles() const;
    void setTitles(const QStringList &newtitles);
    QStringList times() const;
    void setTimes(const QStringList &newtimes);

signals:
    void idChanged();
    void theaterChanged();
    void linkChanged();
    void titlesChanged();
    void timesChanged();

private:
    QString m_id;
    QString m_theater;
    QString m_link;
    QStringList m_titles;
    QStringList m_times;
};

#endif // MOVIESCHEDULEMODEL_H
	
As we can see code above, the class has five properties that could be accessed from QML. The parameters that i used in the code for Q_PROPERTY are :
  • The first paramater is the data type and the property name that QML. We set its data type to QString and the name is id.
  • The second parameter is actually the name of getter function that we use to read the data
  • The third parameter is actually the name of setter function that we use to write the data
  • The fourth paramaeter is actually the name of signal that emitted when the data's value was changed.

After we defined movieschedulemodel in its header file, then we declare its function on movieschedulemodel.cpp. Here is the code:

#include "movieschedulemodel.h"

MovieScheduleModel::MovieScheduleModel(QObject *parent) :
    QObject(parent)
{
}

QString MovieScheduleModel::id() const
{
    return m_id;
}

void MovieScheduleModel::setId(const QString &newid)
{
    m_id = newid;
}

QString MovieScheduleModel::theater() const
{
    return m_theater;
}

void MovieScheduleModel::setTheater(const QString &newtheater)
{
    m_theater = newtheater;
}

QString MovieScheduleModel::link() const
{
    return m_link;
}

void MovieScheduleModel::setLink(const QString &newlink)
{
    m_link = newlink;
}

QStringList MovieScheduleModel::titles() const
{
    return m_titles;
}

void MovieScheduleModel::setTitles(const QStringList &newtitles)
{
    m_titles = newtitles;
}

QStringList MovieScheduleModel::times() const
{
    return m_times;
}

void MovieScheduleModel::setTimes(const QStringList &newtimes)
{
    m_times = newtimes;
}
As we can see from the code above, that this .cpp file contains declaration of functions that we have defined in .h file before.

Then we make the other model class, which is MovieScheduleDetail class. This class is used to place the detail of the movie schedule, e.i. the title of the movie and the time of the movie play. So, here is the code of moviescheduledetail.h:
#ifndef MOVIESCHEDULEDETAIL_H
#define MOVIESCHEDULEDETAIL_H

#include <QObject>

class MovieScheduleDetail : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged)
    Q_PROPERTY(QString time READ time WRITE setTime NOTIFY timeChanged)
public:
    explicit MovieScheduleDetail(QString title, QString time);
    QString title() const;
    QString time() const;
    void setTitle(const QString &newtitle);
    void setTime(const QString &newtime);
signals:
    void titleChanged();
    void timeChanged();

private:
    QString m_title;
    QString m_time;
};

#endif // MOVIESCHEDULEDETAIL_H
This class has two attributes, title and time. we also use Q_PROPERTY macro to make the attributes accessible from QML. And here is the code from moviescheduledetail.cpp :
#include "moviescheduledetail.h"

MovieScheduleDetail::MovieScheduleDetail(QString title, QString time)
{
    m_title = title;
    m_time = time;
}

QString MovieScheduleDetail::title() const{
    return m_title;
}
QString MovieScheduleDetail::time() const{
    return m_time;
}
void MovieScheduleDetail::setTitle(const QString &newtitle){
    m_title = newtitle;
    emit titleChanged();
}

void MovieScheduleDetail::setTime(const QString &newtime){
    m_time = newtime;
    emit timeChanged();
}


Then after we make model classes, then we make MovieScheduleData class. So here is the code from moviescheduledata.h
#ifndef MOVIESCHEDULEDATA_H
#define MOVIESCHEDULEDATA_H

#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QXmlStreamReader>
#include <QtXmlPatterns/QXmlQuery>
#include <QtQml>
#include "movieschedulemodel.h"
#include "qtquick2applicationviewer.h"

class MovieScheduleData : public QObject
{
    Q_OBJECT
public:
    MovieScheduleData(QQmlContext *ctx);
    Q_INVOKABLE void getDetails(QString id);

public slots:
    void replyFinished(QNetworkReply* a);

private:
    QNetworkAccessManager *manager;
    QNetworkRequest request;
    QXmlStreamReader *xmldata;
    QList<QObject*> details;
    QQmlContext *ctxt;

};

#endif // MOVIESCHEDULEDATA_H
This class is not a model class. This class is used to retrieve xml data through network, and then store it to a list of QObject and then set it to the root context of QML.
We make a function that can be accessed in QML, so we type Q_INVOKABLE macro before we define the function name. We also make a slot that would be call after QNetworkAccessManager::finished() signal emitted. This signal emitted when QNetworkAccessManager has been finish retrieve all of the data from network.
Then it has QNetworkAccessManager *manager to maintain the connection, QNetworkRequest request to get data after the network was connected, QXmlStreamReader *xmldata to maintain the xml data, QList<QObject> details to store the data into list, and QQmlContext *ctxt to get root context object from QML and to set context property.
And then, we look to moviescheduledata.cpp:
#include <QDebug>
#include <QStringList>
#include <QBuffer>
#include <QtQml>
#include "moviescheduledata.h"
#include "qtquick2applicationviewer.h"
#include "moviescheduledetail.h"


MovieScheduleData::MovieScheduleData(QQmlContext *ctx)
{
    this->ctxt = ctx;
    manager = new QNetworkAccessManager(this);
    request.setUrl(QUrl("http://www.blitzmegaplex.com/rss/schedule.php"));
    manager->get(request);
    connect(manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(replyFinished(QNetworkReply*)));
}

void MovieScheduleData::replyFinished(QNetworkReply *a)
{
    qDebug()<<"replyFinished\n";
    xmldata = new QXmlStreamReader(a->readAll());
    MovieScheduleModel *detail;
    int flag = 0;
    while(!xmldata->atEnd() && !xmldata->hasError()){
        xmldata->readNext();
        if(flag == 0)detail = new MovieScheduleModel();
        if(xmldata->isStartElement())
        {
            QString name = xmldata->name().toString();
            if( name == "guid")
            {
                detail->setId(xmldata->readElementText());
                flag++;
            }
            if(name == "title")
            {
                detail->setTheater(xmldata->readElementText());
                flag++;
            }
            if(name == "link")
            {
                detail->setLink(xmldata->readElementText());
                flag++;
            }
            if(name == "description")
            {
                QStringList temp,titlesTemp,timesTemp;
                QString title = "",time = "";
                temp = xmldata->readElementText().trimmed().split("\n");
                for(int j=0;j<temp.size();j++)
                {
                    if(temp.at(j).contains("["))
                    {
                        if(title != temp.at(j).trimmed())
                        {
                            title = temp.at(j).trimmed();
                            continue;
                        }
                    }
                    else if(temp.at(j).contains("<br/>"))continue;
                    else
                    {
                        time = temp.at(j).trimmed();
                    }
                    titlesTemp.append(title.contains("'")?title.replace("'","\'"):title);
                    timesTemp.append(time);
                }
                detail->setTitles(titlesTemp);
                detail->setTimes(timesTemp);
                flag++;
            }
            if(flag==4)
            {
                details.append(detail);
                flag = 0;
            }
        }
    }

    if(xmldata->error())
    {
        qDebug()<<"error:"<<xmldata->errorString();
    }
    else
    {
        qDebug()<<"success retrieved all data";
        details.removeAt(0);
        ctxt->setContextProperty("MovieModel",QVariant::fromValue(details));
    }
}

void MovieScheduleData::getDetails(QString id)
{
    QList<QObject*> temp;
    foreach (QObject *a, details )
    {
        if(((MovieScheduleModel*)a)->id() == id)
        {
            for(int i=0;i<((MovieScheduleModel*)a)->titles().size();i++)
            {
                temp.append(new MovieScheduleDetail(((MovieScheduleModel*)a)->titles().at(i),((MovieScheduleModel*)a)->times().at(i)));
            }
            break;
        }
    }
    ctxt->setContextProperty("MovieSchedule",QVariant::fromValue(temp));
}
As we can see from the code above, when the constructor was called, it sets context attribute to the context that had been pass from constructor's paramater. After that, we initialize manager attribute and then set the url that we want to request its data by using QNetworkReply::setUrl(), and then we get the data using QNetworkAccessManager::get(). And after that, we connect the QNetworkAccessManager::finished() signal to MovieScheduleData::replyFinished() slot that we have created before.
Then, we declare the MovieScheduleData::replyFinished() slot. In this slot, we read all of the xml data using QNetworkReply::readAll() and pass it to QXmlStreamReader constructor. We loop the data and filter it by using the tag element name of xml using QXmlStreamReader::name(). Then get the text of the element using QXmlStreamReader::readElementText(). Then we check if the xml data contains error by using QXmlStreamReader::error(), then to know what error was occured by using QXmlStreamReader::errorString(). Then if there is no error, so we place that list of object to the rootContext property by using QQmlContext::setContextProperty().
MovieScheduleData::getDetails() is a method that we use to get the movie details of the theater by using the theater id. After we get the details, then we place it to MovieScheduleDetail object, typecast it to QObject, and then set it to context property using QQmlContext::setContextProperty.

After we make all of the class, then we change main.cpp file. So here is the code:
#include <QtGui/QGuiApplication>
#include <QtQml>
#include <QDebug>
#include "qtquick2applicationviewer.h"
#include "moviescheduledata.h"
#include <Windows.h>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);


    QtQuick2ApplicationViewer viewer;
    QQmlContext *ctxt = viewer.rootContext();
    MovieScheduleData mov(ctxt);
    ctxt->setContextProperty("MovieModel",QVariant::fromValue(QList<QObject*>()));
    ctxt->setContextProperty("MovieSchedule",QVariant::fromValue(QList<QObject*>()));
    ctxt->setContextProperty("MovieScheduleDetails",&mov);
    viewer.setMainQmlFile(QStringLiteral("qml/TestingQt/main.qml"));
    viewer.showExpanded();
    return app.exec();
}
In main.cpp, we get the root context of the qml using QQuickView::rootContext() and place it to QQmlContext *ctxt object. QtQuick2ApplicationViewer class is actually a descendant of QQuickView class, so we can use QQuickView::rootContext() method. After that, we make MovieScheduleData object called mov and set its paramater to ctxt object. And after that, we set default value of the context properties to a empty list of object. And then we set the main QML that run early when the program started.

Start Designing The Application

We have already make the C++ code, now we create the application design using QML. Here is the preview of the design we will make :

gambar1gambar2

First, we make basic design, which is the window, the header, and the footer. Between the header and the footer, we use ListView to list all the content and display it between them. Each list is clickable, and will show different ListView if you click one of them. So here is the code :
import QtQuick 2.0

Rectangle {
    id: mainmenu
    width: 280
    height: 400
    color: "white"
    Rectangle{
        id: banner
        width: parent.width
        height: bannertext.paintedHeight+5
        anchors.top: parent.top
        Text{
            id: bannertext
            width: parent.width
            height: parent.height
            text: "Blitzmegaplex"
            color: "white"
            font{pixelSize: 20}
            horizontalAlignment: Text.AlignHCenter
            verticalAlignment: Text.AlignVCenter
            wrapMode: Text.WordWrap
        }
        color: "darkred"
    }
    Rectangle{
        id: back
        width: parent.width
        height: 20
        anchors.top: banner.bottom
        Text{
            id: backtext
            width: parent.width
            height: parent.height
            text: "Back"
            color: "black"
            font{
                pixelSize: 20
            }
            horizontalAlignment: Text.AlignHCenter
            verticalAlignment: Text.AlignVCenter
        }
        MouseArea{
            anchors.fill: parent
            onClicked: {
                mainmenu.state = "Main";
                bannertext.text = "Blitzmegaplex";
            }
        }
        color: "red"
    }
    Loader{
        id: theloader
        anchors.top: banner.bottom
        anchors.bottom: copyright.top
        anchors.left: parent.left
        anchors.right: parent.right
    }

    Text{
     id: thetext
     visible: false
    }
    Rectangle{
        id: copyright
        width: parent.width
        height: 20
        Text{
            anchors{
                horizontalCenter: parent.horizontalCenter
                verticalCenter: parent.verticalCenter
            }
            text: "Copyright © 2013 by MZ"
            color: "white"
            font{pixelSize: 11}
        }
        color: "darkred"
        anchors.bottom: mainmenu.bottom
    }
    states:[
        State{
            name: "Main"
            PropertyChanges {
                target: theloader
                source: "MainMenu.qml"
                anchors.top: banner.bottom
            }
            PropertyChanges {
                target: back
                visible: false
            }
        },
        State{
            name: "Movie"
            PropertyChanges {
                target: theloader
                source: "MovieScheduleMenu.qml"
                anchors.top: back.bottom
            }
            PropertyChanges {
                target: back
                visible: true
            }
        }
    ]
    Component.onCompleted: mainmenu.state = "Main"
}

We make a rectangle as a container for other components and also for the main window. We name it mainmenu. After that, we make three other rectangles as mainmenu's children. One rectangle as header, one rectangle as back button, and one rectangle as footer. It's all basic property binding, but i will explain the tricky one. See the banner rectangle. It's height value is bannertext.paintedHeight+5. It means that the height of the rectangle depends on bannertext painted height. It happened because we wrap the text of banner (look at bannertext, wrapMode property's value is Text.WordWarp). So if the text is longer than banner's width, than the text will go down and the banner's height adjust to the height of text.
Loader here is used to change the ListView. The trigger is when user click one of the list in the view. When onClick signal emitted, then it changes the state of mainmenu. Mainmenu has two states named Main and Movie. The Main state changes theloader's (Loader's id) source to "MainMenu.qml" and set its anchors.top to banner.bottom. It also changes back's visible to false. The Movie state changes source of theloader to "MovieScheduleMenu.qml" and set its anchor.top to back.bottom. It also changes back's visible property to true.
Component.onCompleted signal is emitted when the qml component is already loaded. So when the signal is emitted, then we set mainmenu's state to "Main".

Then we move to another qml file, called MainMenu.qml. Here is the code:

import QtQuick 2.0

ListView {
    model: MovieModel
    delegate: MenuItem{}
    clip: true
}

This file contains ListView component that show the model data into list. ListView has many orientation, but we use its default setting, which is top to bottom. We set its model to MovieModel. Its model is come from C++ Object list that was set to the root of the context. We set its delegate property to MenuItem component. MenuItem is custom qml component that i made before. We will discuss that component later. Delegate property is a property that manage how the data will be displayed in application. We set clip property to true. It means that we render the listview only on its bounday.

Then we move to another qml file, called MenuItem.qml. Here is the code:

import QtQuick 2.0

Component{
    Rectangle{ 
        id: item
        width: mainmenu.width ; height: 40
        color: "white"
        gradient:grad1
        transform: Rotation{
            id: rot
            origin.x: 0
            origin.y: item.height/2
            angle: -90
            axis.x: 1
            axis.y: 0
            axis.z: 0
        }

        Gradient{
            id: grad1
            GradientStop{position: 0.1; color: "white"}
            GradientStop{position: 0.7; color: "lightgrey"}
            GradientStop{position: 0.9; color: "#E3E3E3"}
        }

        Gradient{
            id: grad2
            GradientStop{position: 0.1; color: "white"}
            GradientStop{position: 0.7; color: "lightgrey"}
            GradientStop{position: 0.9; color: "grey"}
        }

        Text{
            id: itemtext
            text: model.modelData.theater.split(",")[0];
            color: "red"
            anchors.verticalCenter: item.verticalCenter
            font.bold: true
            anchors.left: parent.left
            anchors.leftMargin: 10
        }

        MouseArea{
            anchors.fill: parent
            hoverEnabled: true
            onClicked: {
                MovieScheduleDetails.getDetails(model.modelData.id);
                bannertext.text = itemtext.text;
                mainmenu.state = "Movie";
            }
            onEntered: {
                item.gradient = grad2;
                itemtext.font.underline = true;
            }
            onExited: {
                item.gradient = grad1;
                itemtext.font.underline = false;
            }
        }

        states:[
            State{
                name:"rotate"
                PropertyChanges{
                    target: rot
                    angle: 0
                }
            }
        ]

        transitions:[
            Transition {
                to: "rotate"
                PropertyAnimation{target: rot; properties: "angle"; duration: 500;}
            }
        ]
        Component.onCompleted: item.state = "rotate";
    }
}

In this file. its contains code that manage how the item of data will be displayed in application. So first, we make a Component object. It has rectangle, that means, the item will be displayed in rectangle shape. In this rectangle object, i will explain about gradient, transform, states, and transitions properties. We first look at gradient property. It binds to grad1 value. grad1 is Gradient object that manage color gradient. grad1 has three GradientStop Object. GradientStop manage color distribution. We set its position to 0.1 and the color is white. The range of the gradient is 0.1 to 1.0. It means that we set white color from 0.1 until the next stop. If there is no GradientStop, then it will coloring from the position to the rest.
transform property is a property that manage transformation of rectangle. The origin.x property set to 0, origin.y set to half height of the rectangle. If you know want to know more about Rotation, you can go to this link.
states property is a property that contains many state. We make a state call rotate. This state will change rot's angle property to 0.
transitions property is a property that contains Transitions object. Transation is occurence that will happen when the state changes. We make a transition when the state change to rotate. We will make an animation when rot properties change from -90 to 0. The duration would be 500ms. To make that animation happen, we use PropertyAnimation. And then we set the state to rotate when the component is completely loaded.
In the MouseArea object, we set hoverEnabled to true. It means we can hover the MouseArea. We set anchors.fill to parent. It means MouseArea will cover all over its parent size. On OnClicked signal, we call C++ method called MovieScheduleDetails::getDetails(). The parameter of the method is the id of the list. So to get the id, we can use model.modelData.id. The signal is also change the bannertext.text to the text of the item that user choose. And then, it changes mainmenu.state to Movie. We also declare OnExited and OnEntered signals. Those signals is actually change the gradient property and itemtext.font.underline property.

Now we see another qml file, MovieScheduleMenu.qml. Here is the code:

import QtQuick 2.0

ListView {
    model: MovieSchedule
    delegate: MovieScheduleItem{}
    clip: true
    section {
        property: "title"
        criteria: ViewSection.FullString
        delegate: Rectangle{
            gradient:Gradient{
                GradientStop{position: 0.1; color: "lightgrey"}
                GradientStop{position: 0.3; color: "grey"}
            }
            width: parent.width
            height: 20
            Text{
                id: sec
                width: parent.width
                height: parent.height
                color: "white"
                font{
                    pixelSize: 12
                    bold: true
                }
                text: section
                horizontalAlignment: Text.AlignJustify
                verticalAlignment: Text.AlignVCenter
            }
        }
    }
}

This file is ListView object. Same with MainMenu.qml, but it has different model and delegate. It alse has section property. The section property categorize the data according to section.property value. We set section.property to title. So the model will be categorized by the title of the movie. We set section.criteria value to ViewSection.FullString. It means that the section text will be displayed entirely. We also set its delegate property to rectangle. I already explain you about delegate, and i hope you can understand by looking at the code directly and analyze it by yourself. One thing that you have to know that, the value of Text's text property is section, it means that the text is come from object model which has title property.

After that, we look the last file called MovieScheduleItem.qml. Here is the code:

import QtQuick 2.0

Component{
    Rectangle{
        id: movieitem
        width: mainmenu.width
        height: 20
        color: "white"
        gradient:moviegrad1
        transform: Rotation{
            id: movierot
            origin.x: 0
            origin.y: movieitem.height/2
            angle: -90
            axis.x: 1
            axis.y: 0
            axis.z: 0
        }

        Gradient{
            id: moviegrad1
            GradientStop{position: 0.1; color: "white"}
            GradientStop{position: 0.7; color: "lightgrey"}
            GradientStop{position: 0.9; color: "#E3E3E3"}
        }

        Gradient{
            id: moviegrad2
            GradientStop{position: 0.1; color: "white"}
            GradientStop{position: 0.7; color: "lightgrey"}
            GradientStop{position: 0.9; color: "grey"}
        }

        Text{
            id: movieitemtext
            text: model.modelData.time;
            color: "red"
            anchors.verticalCenter: movieitem.verticalCenter
            font.bold: true
            anchors.left: parent.left
            anchors.leftMargin: 10
        }

        MouseArea{
            anchors.fill: parent
            hoverEnabled: true
            onEntered: {
                movieitem.gradient = moviegrad2;
                movieitemtext.font.underline = true;
            }
            onExited: {
                movieitem.gradient = moviegrad1;
                movieitemtext.font.underline = false;
            }
        }

        states:[
            State{
                name:"movierotate"
                PropertyChanges{
                    target: movierot
                    angle: 0
                }
            }
        ]

        transitions:
            Transition {
                to: "movierotate"
                PropertyAnimation{target: movierot; properties: "angle"; duration: 500;}
            }
        Component.onCompleted: movieitem.state = "movierotate";
    }
}

This code is almost similar to the MenuItem code. So i hope you can easily the code above.

That's all tutorial how to make a simple application that integrates C++ and QML into one nice application. Thank you.

ċ
TestingQt_Final.7z
(708k)
Muhammad Zullidar,
Nov 25, 2013, 2:46 AM