[Tutorial] Koneksi ke basis data sqlite menggunakan Qt4

Tempat nongkrong. Diskusi bebas di luar topik.
User avatar
saa7_go
Posts: 464
Joined: 21 Jan 2011, 23:37
Contact:

[Tutorial] Koneksi ke basis data sqlite menggunakan Qt4

Postby saa7_go » 16 Jan 2012, 06:33

Pada tutorial ini saya akan menunjukkan bagaimana
- melakukan koneksi ke basis data sqlite
- melakukan query : membuat tabel dan menambahkan record(insert)
- menampilkan record menggunakan konsep Model View Controller ala Qt4 (Controller pada Qt4 disebut Delegate), untuk saat ini hanya menyentuh pada konsep Model dan View

Berikut kodenya:
main.cpp

Code: Select all

#include <QApplication>
#include <QMessageBox>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include "form.h"

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

    QSqlDatabase sqliteDB = QSqlDatabase::addDatabase("QSQLITE");
    sqliteDB.setDatabaseName(":memory:");

    if(!sqliteDB.open()) {
        QMessageBox::critical(0, "Error", sqliteDB.lastError().text());
        return 1;
    } else {

        // membuat tabel buku
        QSqlQuery query;
        QString strQuery = "CREATE TABLE buku(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
                "isbn VARCHAR(20) NOT NULL,"
                "judul VARCHAR(100) NOT NULL,"
                "tahun VARCHAR(4) NOT NULL)";

        if(!query.exec(strQuery)) {
            QMessageBox::critical(0, "Error", query.lastError().text());
            return 1;
        }

        // menambahkan beberapa record
        query.exec("INSERT INTO buku(isbn, judul, tahun) "
                   "VALUES('979-763-176-1', 'Panduan Aplikatif Pemrograman GAMBAS', '2006')");
        query.exec("INSERT INTO buku(isbn, judul, tahun) "
                   "VALUES('978-979-29-0162-7','Tuntunan Praktis Membangun Sistem Informasi Akuntansi dengan Visual Basic dan Microsoft SQL Server','2007'");
        query.exec("INSERT INTO buku(isbn, judul, tahun) "
                   "VALUES('979-20-1840-9', '36 Jam Belajar Komputer Delphi 5.0', '2001')");
        query.exec("INSERT INTO buku(isbn, judul, tahun) "
                   "VALUES('979-763-145-1','Membangun Aplikasi Menggunakan Qt Designer dengan Database PostgreSQL/MySQL','2006')");
        query.exec("INSERT INTO buku(isbn, judul, tahun) "
                   "VALUES('979-731-442-1','Panduan Object-Oriented Programming (OOP) Dasar Pemrograman Delphi','2004')");
        query.exec("INSERT INTO buku(isbn, judul, tahun) "
                   "VALUES('979-763-150-8', 'Belajar Sendiri.net dengan Visual C# 2008','2006')");
    }

    Form form;
    form.show();

    return app.exec();
}



form.h

Code: Select all

#ifndef FORM_H
#define FORM_H

#include <QWidget>
class QTableView;

class Form : public QWidget
{
public:
    explicit Form(QWidget *parent = 0);

private:
    QTableView *tableView_;
};

#endif // FORM_H


form.cpp

Code: Select all

#include "form.h"
#include <QTableView>
#include <QVBoxLayout>
#include <QSqlTableModel>

Form::Form(QWidget *parent) :
    QWidget(parent), tableView_(new QTableView)
{
    QVBoxLayout *layout = new QVBoxLayout(this);
    layout->addWidget(tableView_);

    QSqlTableModel *bukuModel = new QSqlTableModel(this);
    bukuModel->setTable("buku");
    bukuModel->select();

    bukuModel->setHeaderData(0, Qt::Horizontal, "ID Buku");
    bukuModel->setHeaderData(1, Qt::Horizontal, "ISBN");
    bukuModel->setHeaderData(2, Qt::Horizontal, "Judul");
    bukuModel->setHeaderData(3, Qt::Horizontal, "Tahun");

    tableView_->setModel(bukuModel);

    setWindowTitle("Tutorial Qt4 dan Basis Data SQLITE");
}


file project qt4, tutorial_db1.pro

Code: Select all

QT += sql

SOURCES += \
    main.cpp \
    form.cpp

HEADERS += \
    form.h

============================================================
Penjelasan untuk main.cpp

Code: Select all

#include <QApplication>
#include <QMessageBox>
#include <QSqlDatabase>
#include <QSqlError>
#include <QSqlQuery>
#include "form.h"

Pertama-tama, kita meng-include-kan header-header agar program ini dapat berjalan dengan lancar.
- QApplication merupakan kelas yang diperlukan jika kita membuat aplikasi Qt4 dengan menggunakan komponen GUI.
- QMessageBox, digunakan untuk menampilkan pesan, dalam aplikasi ini saya gunakan untuk menampilkan pesan error
- QSqlDatabase, digunakan untuk membuat objek database, kelas ini yg berfungsi untuk melakukan koneksi ke basis data
- QSqlError, digunakan untuk menampilkan informasi error yang berhubungan dengan basis data
- form.h berisi kelas Form yang saya buat untuk menampilkan record-record ke dalam bentuk tabel

Code: Select all

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


Tiap aplikasi berbasis Qt4 yang menggunakan komponen-kompen GUI harus ada objek QApplication (hanya cukup 1 saja).

Code: Select all

    QSqlDatabase sqliteDB = QSqlDatabase::addDatabase("QSQLITE");
    sqliteDB.setDatabaseName(":memory:");

Selanjutnya kita membuat objek QSqlDatabase dengan memanggil fungsi statis addDatabase dengan
parameter tipe driver QSQLITE. Jika menggunakan basis data MySQL, dapat diisi dengan QMYSQL.
Kemudian, kita mengeset basis data ke :memory:, artinya basis data SQLITE yang kita gunakan
berada dalam memory (RAM). Bisa juga kita mengeset basis data ke path tertentu misalnya /home/saa7_go/tutorial.db.

Code: Select all

    if(!sqliteDB.open()) {
        QMessageBox::critical(0, "Error", sqliteDB.lastError().text());
        return 1;

Setelah selesai membuat objek QSqlDatabase dan memanggil fungsi-fungsi yang diperlukan, kita perlu
membuka koneksi basis data dengan method open(), jika berhasil akan mengembalikan nilai true,
sebaliknya akan mengembalikan nilai false. Jika hasilnya false, maka baris-baris kode dalam if
akan dieksekusi dan menampilkan error yang muncul dan program akan keluar.

Code: Select all

    } else {

        // membuat tabel buku
        QSqlQuery query;
        QString strQuery = "CREATE TABLE buku(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
                "isbn VARCHAR(20) NOT NULL,"
                "judul VARCHAR(100) NOT NULL,"
                "tahun VARCHAR(4) NOT NULL)";
      
   if(!query.exec(strQuery)) {
              QMessageBox::critical(0, "Error", query.lastError().text());
              return 1;
        }

Jika koneksi berhasil dibuka maka kita akan membuat tabel buku dengan menggunakan QSqlQuery.
Pertama kita membuat objek QSqlQuery, membuat query pada strQuery, dan mengeksekusi query dengan
method exec(). Jika query gagal dieksekusi maka akan menampilkan pesan error dan aplikasi akan berhenti.

Code: Select all

   // menambahkan beberapa record
        query.exec("INSERT INTO buku(isbn, judul, tahun) "
                   "VALUES('979-763-176-1', 'Panduan Aplikatif Pemrograman GAMBAS', '2006')");
        query.exec("INSERT INTO buku(isbn, judul, tahun) "
                   "VALUES('978-979-29-0162-7','Tuntunan Praktis Membangun Sistem Informasi Akuntansi dengan Visual Basic dan Microsoft SQL Server','2007'");
        query.exec("INSERT INTO buku(isbn, judul, tahun) "
                   "VALUES('979-20-1840-9', '36 Jam Belajar Komputer Delphi 5.0', '2001')");
        query.exec("INSERT INTO buku(isbn, judul, tahun) "
                   "VALUES('979-763-145-1','Membangun Aplikasi Menggunakan Qt Designer dengan Database PostgreSQL/MySQL','2006')");
        query.exec("INSERT INTO buku(isbn, judul, tahun) "
                   "VALUES('979-731-442-1','Panduan Object-Oriented Programming (OOP) Dasar Pemrograman Delphi','2004')");
        query.exec("INSERT INTO buku(isbn, judul, tahun) "
                   "VALUES('979-763-150-8', 'Belajar Sendiri.net dengan Visual C# 2008','2006')");
    }

Jika tabel berhasil dibuat, maka kita akan menambahkan beberapa record ke dalam tabel buku.

Code: Select all

    Form form;
    form.show();

    return app.exec();
}

Setelah selesai membuat tabel dan menambahkan record, kita membuat objek Form dan menampilkannya. Selanjutnya kita memasuki main event loop.
================================================================================================

Penjelasan form.h

Code: Select all

#ifndef FORM_H
#define FORM_H

#include <QWidget>
class QTableView;

class Form : public QWidget
{
public:
    explicit Form(QWidget *parent = 0);

private:
    QTableView *tableView_;
};

#endif // FORM_H

Di sini, kita mendefiniskan kelas Form yang diturunkan dari QWidget, untuk itu kita harus
meng-include-kan header QWidget. Di kelas ini kita hanya membuat konstruktor Form
dan 1 anggota data dengan tipe QTableView.
========================================================================

Penjelasan form.cpp

Code: Select all

#include "form.h"
#include <QTableView>
#include <QVBoxLayout>
#include <QSqlTableModel>

Di atas kita meng-include-kan header-header yang diperlukan seperti QTableView, QVBoxLayout,
dan QSqlTableModel.
QTableView merupakan widget yang mengimplementasikan View tabel yang menampilkan
item-item dari sebuah Model.
QSqlTableModel merupakan kelas yang menyediakan Model data yang dapat diubah dari
tabel basis data tunggal.
QVBoxLayout digunakan untuk menata widget secara vertikal.

Code: Select all

Form::Form(QWidget *parent) :
    QWidget(parent), tableView_(new QTableView)
{

Pada konstruktor kelas Form kita memanggil kontruktor parent yaitu QWidget, dan
mendefinisikan objek tableView

Code: Select all

    QVBoxLayout *layout = new QVBoxLayout(this);
    layout->addWidget(tableView_);

Di sini kita membua objek QVBoxLayout dengan parent this, kemudian kita menambahkan widget tableView_.

Code: Select all

    QSqlTableModel *bukuModel = new QSqlTableModel(this);
    bukuModel->setTable("buku");
    bukuModel->select();

Selanjutnya kita membuat objek QSqlTableModel dengan parent this.
Pada contoh aplikasi ini kita membuat tabel buku, maka dari itu kita mengeset bukuModel
dengan tabel buku.
Kita memanggil method select() untuk mengambil record-record tabel buku.

Code: Select all

    bukuModel->setHeaderData(0, Qt::Horizontal, "ID Buku");
    bukuModel->setHeaderData(1, Qt::Horizontal, "ISBN");
    bukuModel->setHeaderData(2, Qt::Horizontal, "Judul");
    bukuModel->setHeaderData(3, Qt::Horizontal, "Tahun");

Di sini kita mengganti header-header data horizontal (kolom tabel) dengan nama yang
lebih sesuai. Nantinya, header-header ini akan ditampilkan melalui tableView.

Code: Select all

    tableView_->setModel(bukuModel);

Agar tableView dapat menampilkan record-record pada tabel buku, kita harus mengeset
model ke bukuModel.

Code: Select all

    setWindowTitle("Tutorial Qt4 dan Basis Data SQLITE");
}

Mengganti title window pada aplikasi.
===========================================================
Untuk meng-compile aplikasi ini, taruh semua file .h, .cpp, dan .pro ke folder.
Melalui terminal, pindah ke direktori tadi, kemudian jalankan perintah berikut

Code: Select all

$ qmake
$ make
User avatar
blackshirt
Posts: 2336
Joined: 02 Jan 2010, 17:00
Location: Solo dan Kebumen
Contact:

Re: [Tutorial] Koneksi ke basis data sqlite menggunakan Qt4

Postby blackshirt » 16 Jan 2012, 07:28

Nah ini pakarnya C++/Qt :D
User avatar
IntegerManual
Posts: 418
Joined: 13 May 2011, 15:36
Location: Kerajaan Loa Bakung
Contact:

Re: [Tutorial] Koneksi ke basis data sqlite menggunakan Qt4

Postby IntegerManual » 16 Jan 2012, 16:15

hormat sama suhu C++/Qt4 FUI :grin:
User avatar
deny26
Posts: 2450
Joined: 03 Jan 2010, 09:24
Location: ubuntu-indonesia.com

Re: [Tutorial] Koneksi ke basis data sqlite menggunakan Qt4

Postby deny26 » 16 Jan 2012, 18:22

mantap mastah :D pantau terus ah..
User avatar
blackshirt
Posts: 2336
Joined: 02 Jan 2010, 17:00
Location: Solo dan Kebumen
Contact:

Re: [Tutorial] Koneksi ke basis data sqlite menggunakan Qt4

Postby blackshirt » 16 Jan 2012, 22:30

Melengkapi sebagai bahan pengayaan yang disampaikan om saa7_go,
kira-kira ini padanan jika diimplementasikan dengan bahasa C

#include
#include
#include
#include
#include
#include "form.h"


header-header files di atas, Q* adalah header file yang umumnya digunakan di Qt Framework, karena di C tidak memerlukan itu semua, yang dibutuhkan adalah header sqlite3.h

Code: Select all

#include <string.h>
#include <stdio.h>
#include <sqlite3.h>

header sqlite3.h ada dalam paket libsqlite3-dev, jadi kalau belum ada, kita harus menginstallnya

Code: Select all

$sudo apt-get install libsqlite3-dev

Kemudian di bagian ini :
QSqlDatabase sqliteDB = QSqlDatabase::addDatabase("QSQLITE");
sqliteDB.setDatabaseName(":memory:");

di atas dijelaskan bahwa ini adalah proses pembuatan object Qsqldatabase dengan database sqlite yang ditempatkan di memory.
Di C bisa digunakan

Code: Select all

sqlite3* dbhandle;

sqlite3_open(":memory:", &dbhandle);

  if(dbhandle == 0) {
    printf("Database could not be open.");
    return 1;
  }

dengan dbhandle adalah sebuah pointer ke struktur sqlite3. dbhandle ini seperti di atas, bisa dibuka dengan fungsi sqlite3_open, dengan sintak yang hampir mirip.
User avatar
blackshirt
Posts: 2336
Joined: 02 Jan 2010, 17:00
Location: Solo dan Kebumen
Contact:

Re: [Tutorial] Koneksi ke basis data sqlite menggunakan Qt4

Postby blackshirt » 16 Jan 2012, 22:52

kemudian dalam query pembuatan tabel
QSqlQuery query;
QString strQuery = "CREATE TABLE buku(id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,"
"isbn VARCHAR(20) NOT NULL,"
"judul VARCHAR(100) NOT NULL,"
"tahun VARCHAR(4) NOT NULL)";

melakukan instantiate object dari Qsqlquery, dan mendefinisikan struktur tablenya.
Secara simpel, dalam C bisa dengan :
Buat sebuah fungsi wrapper dari sql3_exec yang mengeksekusi sebuah query

Code: Select all

void sql_statement(const char* statement) {
  char *errormessage;
  int   ret;

  ret = sqlite3_exec(dbhandle, statement, 0, 0, &errormessage);

  if(ret != SQLITE_OK) {
    printf("Error di statement: %s [%s].\n", statement, errormessage);
  }
}

kemudian nanti di bagian main(), tinggal dipanggil dengan :

Code: Select all

sql_statement("create table book(id INTEGER PRIMARY KEY, isbn VARCHAR(20), judul VARCHAR(100), tahun VARCHAR(4))");


kemudian di bagian menambahkan record ke dalam struktur tabel yang sudah dibuat,
// menambahkan beberapa record
query.exec("INSERT INTO buku(isbn, judul, tahun) "
"VALUES('979-763-176-1', 'Panduan Aplikatif Pemrograman GAMBAS', '2006')");
query.exec("INSERT INTO buku(isbn, judul, tahun) "
"VALUES('978-979-29-0162-7','Tuntunan Praktis Membangun Sistem Informasi Akuntansi dengan Visual Basic dan Microsoft SQL Server','2007'");
query.exec("INSERT INTO buku(isbn, judul, tahun) "
"VALUES('979-20-1840-9', '36 Jam Belajar Komputer Delphi 5.0', '2001')");
query.exec("INSERT INTO buku(isbn, judul, tahun) "
"VALUES('979-763-145-1','Membangun Aplikasi Menggunakan Qt Designer dengan Database PostgreSQL/MySQL','2006')");
query.exec("INSERT INTO buku(isbn, judul, tahun) "
"VALUES('979-731-442-1','Panduan Object-Oriented Programming (OOP) Dasar Pemrograman Delphi','2004')");
query.exec("INSERT INTO buku(isbn, judul, tahun) "
"VALUES('979-763-150-8', 'Belajar Sendiri.net dengan Visual C# 2008','2006')");
}


sama dengan cara seperti di atas, dengan membuat sebuah wrapper insert_book() yang memanfaatkan sql_statement() yang sudah dibuat :

Code: Select all

void insert_book() {
  sql_statement("begin");
  sql_statement("insert into book (isbn,judul,tahun) values('979-763-176-1','Panduan Aplikatif Pemrograman GAMBAS', '2006')");
  sql_statement("insert into book (isbn,judul,tahun) values('978-979-162-7','Tuntunan Membangun SIM Akuntansi dengan VB dan MSQL Server','2007')");
  sql_statement("insert into book (isbn,judul,tahun) values('979-20-1840-9','36 Jam Belajar Komputer Delphi 5.0','2001')");
  sql_statement("insert into book (isbn,judul,tahun) values('979-763-145-1','Membangun Aplikasi Menggunakan Qt Designer dengan Database PostgreSQL/MySQL','2006')");
  sql_statement("insert into book (isbn,judul,tahun) values('979-731-442-1','Panduan Object-Oriented Programming (OOP) Dasar Pemrograman Delphi','2004')");
  sql_statement("insert into book (isbn,judul,tahun) values('979-763-150-8','Belajar Sendiri.net dengan Visual C# 2008','2006')");
  sql_statement("commit");
}


dan di bagian main tinggal memanggil fungsi tersebut

Code: Select all

insert_book();


Kemudian dengan layout tampilannya, karena Qt dengan mudah menyediakannya, di C implementasi dengan cara yang sederhana, bantuan formated output,

buat wrapper untuk query "select * from table"

Code: Select all

void select_statement(const char* statement) {
  char *errormessage;
  int   ret;
  int   numrecords = 0;

  row = 1;

  ret = sqlite3_exec(dbhandle, statement, select_callback, &numrecords, &errormessage);

  if(ret!=SQLITE_OK) {
    printf("Error di dalam statement select %s [%s].\n", statement, errormessage);
  }
  else {
    printf("\n Jumlah record :  %d \n", numrecords);
  }
}


Kalau dilihat argumen ketiga dari sqlite3_exec adalah sebuah callback function. Untuk lengkapnya bisa baca ini http://www.sqlite.org/c3ref/exec.html..
Kita bisa mendefinisikannya sendiri

Code: Select all

int row;

int select_callback(void *data, int jumlah_fields, char **data_fields, char **kolom) {

  int i;
  int *res = (int*)data;

  if (row) {
    row = 0;
   
    printf("%2s\t%8s\t\t%20s\t\t\t\t\t%s\t\t", kolom[0], kolom[1], kolom[2], kolom[3]);
    printf("\n");

    for(i=0; i< jumlah_fields*30; i++) {
      printf("=");
    }
    printf("\n");
  }

  (*res)++;
 
  printf("%2s \t%8s \t%25s\t\t\t \t\t%s\t\t", data_fields[0], data_fields[1], data_fields[2], data_fields[3]);
  printf("\n");
  return 0;
}

Gunanya adalah menampilkan header dari nama masing-masing kolom dari tabel yang kita buat beserta datanya.
dan di bagian main(), kita bisa menggunakannnya

Code: Select all

select_statement("select * from book asc limit 100");


dan jangan lupa, di bagian akhir main(), untuk merelease dbhandle dan mengembalikannya ke sistem

Code: Select all

sqlite3_close(dbhandle);
return 0;


Kira-kira full listing sourcenya sbb :

Code: Select all


#include <string.h>
#include <stdio.h>
#include <sqlite3.h>

sqlite3* dbhandle;

int row;

int select_callback(void *data, int jumlah_fields, char **data_fields, char **kolom) {

  int i;
  int *res = (int*)data;

  if (row) {
    row = 0;
   
    printf("%2s\t%8s\t\t%20s\t\t\t\t\t%s\t\t", kolom[0], kolom[1], kolom[2], kolom[3]);
    printf("\n");

    for(i=0; i< jumlah_fields*30; i++) {
      printf("=");
    }
    printf("\n");
  }

  (*res)++;
 
  printf("%2s \t%8s \t%25s\t\t\t \t\t%s\t\t", data_fields[0], data_fields[1], data_fields[2], data_fields[3]);
  printf("\n");
  return 0;
}

void select_statement(const char* statement) {
  char *errormessage;
  int   ret;
  int   numrecords = 0;

  row = 1;

  ret = sqlite3_exec(dbhandle, statement, select_callback, &numrecords, &errormessage);

  if(ret!=SQLITE_OK) {
    printf("Error di dalam statement select %s [%s].\n", statement, errormessage);
  }
  else {
    printf("\n Jumlah record :  %d \n", numrecords);
  }
}

void sql_statement(const char* statement) {
  char *errormessage;
  int   ret;

  ret = sqlite3_exec(dbhandle, statement, 0, 0, &errormessage);

  if(ret != SQLITE_OK) {
    printf("Error di statement: %s [%s].\n", statement, errormessage);
  }
}

void insert_book() {
  sql_statement("begin");
  sql_statement("insert into book (isbn,judul,tahun) values('979-763-176-1','Panduan Aplikatif Pemrograman GAMBAS', '2006')");
  sql_statement("insert into book (isbn,judul,tahun) values('978-979-162-7','Tuntunan Membangun SIM Akuntansi dengan VB dan MSQL Server','2007')");
  sql_statement("insert into book (isbn,judul,tahun) values('979-20-1840-9','36 Jam Belajar Komputer Delphi 5.0','2001')");
  sql_statement("insert into book (isbn,judul,tahun) values('979-763-145-1','Membangun Aplikasi Menggunakan Qt Designer dengan Database PostgreSQL/MySQL','2006')");
  sql_statement("insert into book (isbn,judul,tahun) values('979-731-442-1','Panduan Object-Oriented Programming (OOP) Dasar Pemrograman Delphi','2004')");
  sql_statement("insert into book (isbn,judul,tahun) values('979-763-150-8','Belajar Sendiri.net dengan Visual C# 2008','2006')");
  sql_statement("commit");
}

int main() {

 sqlite3_open(":memory:", &dbhandle);

  if(dbhandle == 0) {
    printf("Database could not be open.");
    return 1;
  }

  sql_statement("create table book(id INTEGER PRIMARY KEY, isbn VARCHAR(20), judul VARCHAR(100), tahun VARCHAR(4))");

  insert_book();

  select_statement("select * from book asc limit 100");

  sqlite3_close(dbhandle);
  return 0;
}


compile code tersebut

Code: Select all

$gcc -o testsqlite testsqlite.c -lsqlite3

$ ./testsqlite
id       isbn                     judul               tahun      
========================================================================================================================
 1    979-763-176-1    Panduan Aplikatif Pemrograman GAMBAS                2006      
 2    978-979-162-7    Tuntunan Membangun SIM Akuntansi dengan VB dan MSQL Server                2007
 3    979-20-1840-9    36 Jam Belajar Komputer Delphi 5.0                2001      
 4    979-763-145-1    Membangun Aplikasi Menggunakan Qt Designer dengan Database PostgreSQL/MySQL             2006      
 5    979-731-442-1    Panduan Object-Oriented Programming (OOP) Dasar Pemrograman Delphi                2004      
 6    979-763-150-8    Belajar Sendiri.net dengan Visual C# 2008                2006      

 Jumlah record :  6
User avatar
blackshirt
Posts: 2336
Joined: 02 Jan 2010, 17:00
Location: Solo dan Kebumen
Contact:

Re: [Tutorial] Koneksi ke basis data sqlite menggunakan Qt4

Postby blackshirt » 16 Jan 2012, 22:55

maaf om saa7_go, judul sampelnya ane rubah biar lebih pendek :D

Return to “Pembicaraan Bebas”

Who is online

Users browsing this forum: No registered users and 57 guests