Меnu:


Aplicativo Rápido usando wxwidgets

Desde la época en la que usábamos y programábamos para Palm, una de las características mas distintivas de la Palm era el calendario, lo utilizábamos principalmente para guardar los eventos mas resaltan tes, luego con el global search de Palm, podíamos buscar muy rápidamente los eventos deseados. Hace ya poco mas de 3 años decidimos hacer algo muy similar en ror. Y la verdad es bien fácil hacerlo, con el inconveniente de no poder llevar la información en la palma de la mano, como si sucedía con la Palm; cada vez que deseábamos guardar un nuevo evento teníamos que volver a ejecutar el app(iniciar mi servidor), y no nos podíamos concentrar de lleno en la tarea que deseábamos registrar; de paso tenia que recordar en que carpeta estaba el app y cual era el comando para iniciar la ejecución. Necesitábamos con urgencia algo mas fácil de usar.

Visto el problema en el que nos encontrábamos decidimos poner en practica la experiencia que tenemos en el desarrollo con wxwidgets, para migrar el timeline de ror, a una versión multiplataforma en wxwidgets; la gran ventaja radica en que finalmente podremos poner el timeline en el n800(la palma de la mano), solo sera necesario mover el archivo de la base de datos (sqlite), y tendremos el aplicativo en nuestro escritorio nuevamente. Pongamos manos a la obra para hacer nuestro timeline en wxwidgets.

Que es lo que necesitaremos

En kipuamutay nuestro ambiente de desarrollo primario esta en Archlinux; sin embargo los pasos que detallaremos se podrán efectuar en cualquier otra distribución GNU/Linux y también en win32. Como prerrequisitos necesitamos:

TimeLine

Nuestro aplicativo consiste en 1 frame y 1 dialogo(usando terminología wxWidgets), dentro del frame habrá una caja de texto para buscar los eventos coincidentes con la palabra por la que deseamos filtrar , en el dialogo es donde se vera el detalle del evento seleccionado, nuestra dialog también servirá para agregar nuevos eventos.

Manitas a trabajar (o deditos)

Comenzaremos por definir nuestro aplicativo:

timeline.cpp

#include <wx/app.h>
#include "TimeLine.h"
#include "sqlite3pp.h"


using namespace std;

sqlite3pp::database db;

class TimeLineApp: public wxApp
{
  virtual bool OnInit();
  virtual int OnExit();

};

IMPLEMENT_APP(TimeLineApp)

bool TimeLineApp::OnInit()
{
  db.connect("timelines.sqlite3");
  sqlite3pp::transaction xct(db, true);

  TimeLine *frame = new TimeLine(NULL, wxID_ANY, _T("kipuamutay.com"), wxDefaultPosition, wxDefaultSize);
  frame->set_properties();
  frame->do_layout();

  frame->do_load_data();

  frame->Show(true);
  SetTopWindow(frame);
  return true;

}

int TimeLineApp::OnExit()
{
  db.disconnect();
}


Mirando nuestra de aplicativo, nos concentramos en las lineas de los include, vemos que necesitaremos el sqlite3pp, una vez descargado descomprimirlo en una subcarpeta dentro de la carpeta donde esta nuestro código . Mas adelante agregaremos el archivo TimeLine.h, ahora agregaremos una librería en c que nos facilitara el manejo de las cadenas al momento de hacer los consultas a la base de datos. La librería es buffer (libre de usar) (ubicarla dentro de una carpeta denominada generic). La versión que ponemos a su disposición a continuación es la librería buffer ligeramente modificada, si desea ver los cambios agregados puede el lector descargarla y ver los cambios.
/* $OpenBSD: buffer.c,v 1.32 2010/02/09 03:56:28 djm Exp $ */
/*
 * Author: Tatu Ylonen <ylo@cs.hut.fi>
 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
 * All rights reserved
 * Functions for manipulating fifo buffers (that can grow if needed).
 *
 * As far as I am concerned, the code I have written for this software
 * can be used freely for any purpose. Any derived versions of this
 * software must be clearly marked as such, and if the derived work is
 * incompatible with the protocol description in the RFC file, it must be
 * called by a name other than "ssh" or "Secure Shell".
 */

//#include "includes.h"

//#include <sys/param.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

//#include "xmalloc.h"
#include "buffer.h"
      //#include "log.h"

#define BUFFER_MAX_CHUNK 0x100000
#define BUFFER_MAX_LEN 0xa00000
#define BUFFER_ALLOCSZ 0x008000

Buffer *
buffer_alloc(size_t count )
{
  Buffer *buffer;
  const unsigned int len = 64;
  count = (count < len) ? len : count;

        /*
         * Crear un objeto buffer
         */
  buffer = (Buffer*)malloc(sizeof(struct _buffer_s));
  if (buffer) {
    buffer->alloc = 0;
    buffer->buf = malloc(count);
    buffer->alloc = count;
    buffer->offset = 0;
    buffer->end = 0;
    if (buffer->buf)
      return buffer;
    buffer_free(buffer);
  }
  return NULL;
}

/* Frees any memory used for the buffer. */

void
buffer_free(Buffer *buffer)
{
  if (buffer) {
    if (buffer->alloc > 0) {
      memset(buffer->buf, 0, buffer->alloc);
      buffer->alloc = 0;
      free(buffer->buf);
    }
    memset(buffer, 0, sizeof(struct _buffer_s));
    free(buffer);
  }
}

/*
 * Clears any data from the buffer, making it empty. This does not actually
 * zero the memory.
 */

void
buffer_clear(Buffer *buffer)
{
  buffer->offset = 0;
  buffer->end = 0;
}

/* Appends data to the buffer, expanding it if necessary. */

void
buffer_append(Buffer *buffer, const void *data, unsigned int len)
{
  void *p;
  p = buffer_append_space(buffer, len);
  memcpy(p, data, len);
}

void
buffer_append_string(Buffer *buffer, const char *string)
{
  if (string)
    buffer_append(buffer, string, strlen(string));
}

/* static int */
/* buffer_compact(Buffer *buffer) */
/* { */
/*   /\* */
/*    * If the buffer is quite empty, but all data is at the end, move the */
/*    * data to the beginning. */
/*    *\/ */
/*   if (buffer->offset > MIN(buffer->alloc, BUFFER_MAX_CHUNK)) { */
/*     memmove(buffer->buf, buffer->buf + buffer->offset, */
/*             buffer->end - buffer->offset); */
/*     buffer->end -= buffer->offset; */
/*     buffer->offset = 0; */
/*     return (1); */
/*   } */
/*   return (0); */
/* } */

/*
 * Appends space to the buffer, expanding the buffer if necessary. This does
 * not actually copy the data into the buffer, but instead returns a pointer
 * to the allocated region.
 */

void *
buffer_append_space(Buffer *buffer, unsigned int len)
{
  unsigned int newlen;
  void *p;

  if (len > BUFFER_MAX_CHUNK)
    fprintf(stderr, "buffer_append_space: len %u not supported", len);

  /* If the buffer is empty, start using it from the beginning. */
  if (buffer->offset == buffer->end) {
    buffer->offset = 0;
    buffer->end = 0;
  }
restart:
  /* If there is enough space to store all data, store it now. */
  if (buffer->end + len < buffer->alloc) {
    p = buffer->buf + buffer->end;
    buffer->end += len;
    return p;
  }

  /* Compact data back to the start of the buffer if necessary */
  /* if (buffer_compact(buffer)) */
  /*   goto restart; */

  /* Increase the size of the buffer and retry. */
  //newlen = roundup(buffer->alloc + len, BUFFER_ALLOCSZ);
  newlen = buffer->alloc + len;
  if (newlen > BUFFER_MAX_LEN)
    fprintf(stderr, "buffer_append_space: alloc %u not supported",
          newlen);
  buffer->buf = realloc(buffer->buf, newlen);
  buffer->alloc = newlen;
  goto restart;
  /* NOTREACHED */
}

/*
 * Check whether an allocation of 'len' will fit in the buffer
 * This must follow the same math as buffer_append_space
 */
/* int */
/* buffer_check_alloc(Buffer *buffer, unsigned int len) */
/* { */
/*   if (buffer->offset == buffer->end) { */
/*     buffer->offset = 0; */
/*     buffer->end = 0; */
/*   } */
/* restart: */
/*   if (buffer->end + len < buffer->alloc) */
/*     return (1); */
/*   /\* if (buffer_compact(buffer)) *\/ */
/*   /\*   goto restart; *\/ */
/*   if (roundup(buffer->alloc + len, BUFFER_ALLOCSZ) <= BUFFER_MAX_LEN) */
/*     return (1); */
/*   return (0); */
/* } */

/* Returns the number of bytes of data in the buffer. */

unsigned int
buffer_len(const Buffer *buffer)
{
  return buffer->end - buffer->offset;
}

/* Gets data from the beginning of the buffer. */

int
buffer_get_ret(Buffer *buffer, void *buf, unsigned int len)
{
  if (len > buffer->end - buffer->offset) {
    error("buffer_get_ret: trying to get more bytes %d than in buffer %d",
          len, buffer->end - buffer->offset);
    return (-1);
  }
  memcpy(buf, buffer->buf + buffer->offset, len);
  buffer->offset += len;
  return (0);
}

void
buffer_get(Buffer *buffer, void *buf, unsigned int len)
{
  if (buffer_get_ret(buffer, buf, len) == -1)
    fprintf(stderr, "buffer_get: buffer error");
}

/* Consumes the given number of bytes from the beginning of the buffer. */

int
buffer_consume_ret(Buffer *buffer, unsigned int bytes)
{
  if (bytes > buffer->end - buffer->offset) {
    error("buffer_consume_ret: trying to get more bytes than in buffer");
    return (-1);
  }
  buffer->offset += bytes;
  return (0);
}

void
buffer_consume(Buffer *buffer, unsigned int bytes)
{
  if (buffer_consume_ret(buffer, bytes) == -1)
    fprintf(stderr, "buffer_consume: buffer error");
}

/* Consumes the given number of bytes from the end of the buffer. */

int
buffer_consume_end_ret(Buffer *buffer, unsigned int bytes)
{
  if (bytes > buffer->end - buffer->offset)
    return (-1);
  buffer->end -= bytes;
  return (0);
}

void
buffer_consume_end(Buffer *buffer, unsigned int bytes)
{
  if (buffer_consume_end_ret(buffer, bytes) == -1)
    fprintf(stderr, "buffer_consume_end: trying to get more bytes than in buffer");
}

/* Returns a pointer to the first used byte in the buffer. */

void *
buffer_ptr(const Buffer *buffer)
{
  return buffer->buf + buffer->offset;
}

/* Dumps the contents of the buffer to stderr. */

void
buffer_dump(const Buffer *buffer)
{
  unsigned int i;
  unsigned char *ucp = buffer->buf;

  for (i = buffer->offset; i < buffer->end; i++) {
    fprintf(stderr, "%02x", ucp[i]);
    if ((i-buffer->offset)%16==15)
      fprintf(stderr, "\r\n");
    else if ((i-buffer->offset)%2==1)
      fprintf(stderr, " ");
  }
  fprintf(stderr, "\r\n");
}

buffer.h 3

/* $OpenBSD: buffer.h,v 1.21 2010/08/31 11:54:45 djm Exp $ */

/*
 * Author: Tatu Ylonen <ylo@cs.hut.fi>
 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
 * All rights reserved
 * Code for manipulating FIFO buffers.
 *
 * As far as I am concerned, the code I have written for this software
 * can be used freely for any purpose. Any derived versions of this
 * software must be clearly marked as such, and if the derived work is
 * incompatible with the protocol description in the RFC file, it must be
 * called by a name other than "ssh" or "Secure Shell".
 */

#ifndef BUFFER_H
#define BUFFER_H

typedef struct _buffer_s{
  unsigned char *buf; /* Buffer for data. */
  unsigned int alloc; /* Number of bytes allocated for data. */
  unsigned int offset; /* Offset of first byte containing data. */
  unsigned int end; /* Offset of last byte containing data. */
} Buffer;

#ifdef __cplusplus
extern "C"
{
#endif /* __cplusplus */

Buffer *buffer_alloc(size_t count);
void buffer_clear(Buffer *);
void buffer_free(Buffer *);

unsigned int buffer_len(const Buffer *);
void *buffer_ptr(const Buffer *);

void buffer_append(Buffer *, const void *, unsigned int);
void buffer_append_string(Buffer *buffer, const char *string);

void *buffer_append_space(Buffer *, unsigned int);

int buffer_check_alloc(Buffer *, unsigned int);

void buffer_get(Buffer *, void *, unsigned int);

void buffer_consume(Buffer *, unsigned int);
void buffer_consume_end(Buffer *, unsigned int);

void buffer_dump(const Buffer *);

int buffer_get_ret(Buffer *, void *, unsigned int);
int buffer_consume_ret(Buffer *, unsigned int);
int buffer_consume_end_ret(Buffer *, unsigned int);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* BUFFER_H */

Como lo habíamos ya expresado

TimeLine.cpp

#include "TimeLine.h"
#include "TimeLineDetail.h"
#include "sqlite3pp.h"
#include "buffer.h"

static void
fill_entity_list( wxListCtrl* p_list_box, const char * search_string );
int GetSelectedItemText(wxListCtrl* p_list_box, int selectedIndex,  int list_column_idx);

using namespace std;

extern sqlite3pp::database db;

BEGIN_EVENT_TABLE(TimeLine, wxFrame)
EVT_BUTTON(wxID_ADD, TimeLine::OnAdd)
EVT_BUTTON(wxID_DELETE, TimeLine::OnDelete)
EVT_BUTTON(wxID_OK, TimeLine::OnOK)
EVT_BUTTON(wxID_CANCEL, TimeLine::OnCancel)
EVT_CLOSE (TimeLine::OnBack)
END_EVENT_TABLE();

TimeLine::TimeLine(wxWindow* parent, int id, const wxString& title, const wxPoint& pos, const wxSize& size, long style):
  wxFrame(parent, id, title, pos, size, wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL)
{
  panel = new wxPanel(this, wxID_ANY);
  lblSearch = new wxStaticText(panel, wxID_ANY, wxT("Search"));
  txtSearch = new wxTextCtrl(panel, wxID_ANY, wxEmptyString);
  lstItems = new wxListCtrl(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER|wxLC_REPORT | wxLC_SINGLE_SEL);
  btnAdd = new wxBitmapButton(panel, wxID_ADD, wxArtProvider::GetBitmap( wxART_ADD_BOOKMARK  ));
  btnDel = new wxBitmapButton( panel, wxID_DELETE, wxArtProvider::GetBitmap( wxART_DEL_BOOKMARK  ));
  btnOk = new wxButton(panel, wxID_OK, wxT("OK"));
  btnCancel = new wxButton(panel, wxID_CANCEL, wxT("Cancel"));
  statusbar = CreateStatusBar(1, 0);

}


void TimeLine::set_properties()
{
  lstItems->InsertColumn( 0,
                          _("name"),
                          wxLIST_FORMAT_LEFT,
                          (350));
  lstItems->InsertColumn( 1,
                          wxEmptyString,
                          wxLIST_FORMAT_LEFT,
                          (0));//0 is hide
  lstItems->SetMinSize(wxSize(350, 450));
  btnAdd->SetSize(btnAdd->GetBestSize());
  btnDel->SetSize(btnDel->GetBestSize());
  txtSearch->SetMaxLength(128);
}


void TimeLine::do_layout()
{
  wxBoxSizer* top_sizer_trick = new wxBoxSizer(wxVERTICAL);
  wxBoxSizer* panel_ordering_sizer = new wxBoxSizer(wxVERTICAL);
  wxBoxSizer* border_settings_sizer = new wxBoxSizer(wxVERTICAL);
  wxFlexGridSizer* object_1 = new wxFlexGridSizer(1, 4, 0, 5);
  wxGridSizer* search_header = new wxGridSizer(1, 2, 0, 0);
  search_header->Add(lblSearch, 0, 0, 0);
  search_header->Add(txtSearch, 0, 0, 0);
  border_settings_sizer->Add(search_header, 0, 0, 0);
  border_settings_sizer->Add(0, 10, 0, 0, 0);
  border_settings_sizer->Add(lstItems, 0, wxALL|wxEXPAND, 0);
  border_settings_sizer->Add(0, 10, 0, 0, 0);
  object_1->Add(btnAdd, 0, 0, 0);
  object_1->Add(btnDel, 0, 0, 0);
  object_1->Add(btnOk, 0, 0, 0);
  object_1->Add(btnCancel, 0, 0, 0);
  border_settings_sizer->Add(object_1, 0, 0, 0);
  panel_ordering_sizer->Add(border_settings_sizer, 1, wxALL|wxEXPAND, 5);
  panel->SetSizer(panel_ordering_sizer);
  top_sizer_trick->Add(panel, 0, 0, 0);
  SetSizer(top_sizer_trick);
  top_sizer_trick->Fit(this);
  Layout();
}

void TimeLine::do_load_data()
{
  fill_entity_list(lstItems , NULL );
  txtSearch->SetFocus();
  txtSearch->Connect( wxID_ANY, wxEVT_KEY_UP,
                      wxKeyEventHandler(TimeLine::OnKeyUpText),
                      NULL, this);
  lstItems->Connect(  wxEVT_COMMAND_LIST_ITEM_ACTIVATED,
                      wxListEventHandler(TimeLine::OnItemActivated),
                      NULL, this);
  lstItems->Connect(wxID_ANY, wxEVT_KEY_DOWN, wxKeyEventHandler(TimeLine::OnKeyDownListCtrl), (wxObject*)0, this);

}

void TimeLine::OnAdd(wxCommandEvent& event)
{
  Buffer *query;
  TimeLineDetail tld_dlg( this, wxID_ANY, _T("time line detail"),
                          wxPoint(-1, -1), wxSize(-1, -1), wxDEFAULT_DIALOG_STYLE );

  if ( tld_dlg.ShowModal() == wxID_OK ) {
    query = buffer_alloc(64);
    try {
      buffer_append_string(query, "insert into time_lines (date, title, description, created_at) values(");
      buffer_append_string(query, "date('");
      buffer_append_string(query, (const char *)tld_dlg.m_datePicker1->GetValue().Format(wxT("%Y-%m-%d")).char_str());
      buffer_append_string(query, "'), '");
      buffer_append_string(query, tld_dlg.txtTitle->GetValue().mb_str(wxConvUTF8));
      buffer_append_string(query, "', '");
      buffer_append_string(query, tld_dlg.txtDesc->GetValue().mb_str(wxConvUTF8));
      buffer_append_string(query, "', datetime('now', 'localtime'));");
      buffer_append_space(query, 1);
      //fprintf(stdout, "qry->%s\n", (const char *)buffer_ptr(query));
      db.execute((const char *)buffer_ptr(query));
      this->RefreshList();
    }
    catch (exception& ex) {
      cout << ex.what() << endl;
    }
    buffer_free(query);
  }

  event.Skip();
}

void TimeLine::OnDelete(wxCommandEvent& event)
{
  int selectedIndex = -1;

  selectedIndex = lstItems->GetNextItem(selectedIndex,
                                        wxLIST_NEXT_ALL,
                                        wxLIST_STATE_SELECTED);
  if (selectedIndex >= 0) {
    if ( wxMessageBox(wxString::FromUTF8("Sure, about deletion?"),
                      wxString::FromUTF8("validation"),
                      wxICON_QUESTION | wxYES_NO, this) == wxYES  )
      {
        this->DeleteFromDB(selectedIndex);
      }
  }


  event.Skip();
  return;

}

void TimeLine::OnOK(wxCommandEvent& WXUNUSED(event))
{
  return;
}

void TimeLine::OnBack(wxCloseEvent &WXUNUSED(event))
{
  this->Destroy();
}

void TimeLine::OnCancel(wxCommandEvent& WXUNUSED(event))
{
  this->Close();

}

void TimeLine::RefreshList(void)
{
  lstItems->DeleteAllItems();
  fill_entity_list(lstItems, (const char *)txtSearch->GetValue().mb_str(wxConvUTF8) );

}

void TimeLine::OnKeyUpText( wxKeyEvent &event )
{
  int key_code = event.GetKeyCode();

  if ( (!txtSearch->IsEmpty() && isalnum(key_code)) ||  (key_code==WXK_BACK || key_code==WXK_DELETE)   ) {
    lstItems->DeleteAllItems();
    fill_entity_list(lstItems, (const char *)txtSearch->GetValue().mb_str(wxConvUTF8) );

    return;
  }

  event.Skip();

  return;
}

void TimeLine::OnItemActivated(wxListEvent &event)
{
  int selectedIndex = event.GetIndex();
  wxString selectedCode;
  Buffer *query;

  if (selectedIndex >= 0) {
    selectedCode = wxString::Format(_T("%d"), GetSelectedItemText(lstItems, selectedIndex, 1));
    TimeLineDetail tld_dlg( this, wxID_ANY, _T("time line detail-edition"),
                            wxPoint(-1, -1), wxSize(-1, -1), wxDEFAULT_DIALOG_STYLE, selectedCode );

    if ( tld_dlg.ShowModal() == wxID_OK ) {
      query = buffer_alloc(64);
      try {
        buffer_append_string(query, "update time_lines set title='");
        buffer_append_string(query, tld_dlg.txtTitle->GetValue().mb_str(wxConvUTF8));
        buffer_append_string(query, "', description='");
        buffer_append_string(query, tld_dlg.txtDesc->GetValue().mb_str(wxConvUTF8));
        buffer_append_string(query, "', updated_at=datetime('now', 'localtime') where id=");
        buffer_append_string(query, selectedCode.mb_str(wxConvUTF8));
        buffer_append_string(query, ";");
        buffer_append_space(query, 1);
        db.execute((const char *)buffer_ptr(query));
        this->RefreshList();
      }
      catch (exception& ex) {
        cout << ex.what() << endl;
      }
      buffer_free(query);
    }

  }
  event.Skip();
}

void TimeLine::OnKeyDownListCtrl(wxKeyEvent &event)
{
  int selectedIndex = -1;

  selectedIndex = lstItems->GetNextItem(selectedIndex,
                                        wxLIST_NEXT_ALL,
                                        wxLIST_STATE_SELECTED);

  if( event.GetKeyCode() == WXK_DELETE ) {
    if (selectedIndex >= 0) {
      if ( wxMessageBox(wxString::FromUTF8("Sure, about deletion?"),
                        wxString::FromUTF8("validation"),
                        wxICON_QUESTION | wxYES_NO, this) == wxYES  )
        {
          this->DeleteFromDB(selectedIndex);
        }
    }
  }


  event.Skip();
  return;
}

void TimeLine::DeleteFromDB(int selectedIndex)
{
  wxString selectedCode;
  Buffer *query;

  selectedCode = wxString::Format(_T("%d"), GetSelectedItemText(lstItems, selectedIndex, 1));
  query = buffer_alloc(64);

  try {
    buffer_append_string(query, "delete from time_lines where id=");
    buffer_append_string(query, selectedCode.char_str());
    buffer_append_string(query, ";");
    buffer_append_space(query, 1);
    db.execute((const char *)buffer_ptr(query));
    this->RefreshList();
  }
  catch (exception& ex) {
    cout << ex.what() << endl;
  }
  buffer_free(query);

  return;
}

static void
fill_entity_list( wxListCtrl* p_list_box, const char * search_string ) {
  long            insertIndex = 0;
  wxString        strCode;
  wxString        strDescription;
  int id;
  char const* title;
  Buffer *query;


  query = buffer_alloc(64);

  try {

    buffer_append_string(query, "SELECT id, date(date) || ', ' || title FROM time_lines");
    if (search_string != NULL  ) {
      buffer_append_string(query, " where title like '%");
      buffer_append_string(query, search_string);
      buffer_append_string(query, "%' or ");
      buffer_append_string(query, " description like '%");
      buffer_append_string(query, search_string);
      buffer_append_string(query, "%' or ");
      buffer_append_string(query, " id like '%");
      buffer_append_string(query, search_string);
      buffer_append_string(query, "%' ");
    }
    buffer_append_string(query, " order by date desc ;");
    buffer_append_space(query, 1);
    sqlite3pp::query qry(db, (const char *)buffer_ptr(query));

    for (sqlite3pp::query::iterator i = qry.begin(); i != qry.end(); ++i) {
      boost::tie(id, title) = (*i).get_columns<int, char const*>(0, 1);
      strCode = wxString::Format(_T("%d"), id);
      strDescription = wxString::FromUTF8(title);
      p_list_box->InsertItem(insertIndex, strDescription );
      p_list_box->SetItem(insertIndex, 1, strCode );

      insertIndex++;
    }
  }
  catch (exception& ex) {
    cout << ex.what() << endl;
  }
  buffer_free(query);

  return;
}

int GetSelectedItemText(wxListCtrl* p_list_box, int selectedIndex,  int list_column_idx) {
  wxListItem     row_info;

  // Set what row it is (m_itemId is a member of the regular wxListCtrl class)
  row_info.m_itemId = selectedIndex;
  // Set what column of that row we want to query for information.
  row_info.m_col = list_column_idx;
  // Set text mask
  row_info.m_mask = wxLIST_MASK_TEXT;

  // Get the info and store it in row_info variable.
  p_list_box->GetItem( row_info );

  // Extract the text out that cell
  return atoi(row_info.GetText().char_str());
}

TimeLine.h

#include <wx/wx.h>
#include <wx/image.h>
#include <wx/listctrl.h>
#include <wx/artprov.h>


#ifndef TIMELINE_H
#define TIMELINE_H


class TimeLine: public wxFrame {
 public:

  TimeLine(wxWindow* parent, int id, const wxString& title, const wxPoint& pos=wxDefaultPosition, const wxSize& size=wxDefaultSize, long style=wxDEFAULT_FRAME_STYLE);

 private:

 protected:

  wxStaticText* lblSearch;
  wxTextCtrl* txtSearch;
  wxListCtrl* lstItems;
  wxBitmapButton* btnAdd;
  wxBitmapButton* btnDel;
  wxButton* btnOk;
  wxButton* btnCancel;
  wxPanel* panel;
  wxStatusBar* statusbar;
  void OnBack(wxCloseEvent &WXUNUSED(event));
  void OnCancel(wxCommandEvent& WXUNUSED(event));
  void OnAdd(wxCommandEvent& event);
  void OnDelete(wxCommandEvent& event);
  void OnOK(wxCommandEvent& WXUNUSED(event));
  void OnItemActivated(wxListEvent &event);
  void OnKeyDownListCtrl(wxKeyEvent &event);
  void DeleteFromDB(int selectedIndex);

 public:
  void set_properties();
  void do_layout();
  void do_load_data();
  void RefreshList(void);
  void OnKeyUpText( wxKeyEvent &event );

  DECLARE_EVENT_TABLE();

};

#endif // TIMELINE_H

Habiendo concluido con nuestro frame. Es tiempo de codificar el dialogo

TimeLineDetail.cpp

#include <wx/datetime.h>
#include "TimeLineDetail.h"
#include "sqlite3pp.h"
#include "buffer.h"

using namespace std;

extern sqlite3pp::database db;


TimeLineDetail::TimeLineDetail(wxWindow* parent, int id, const wxString& title, const wxPoint& pos, const wxSize& size, long style, const wxString& selected_code):
  wxDialog(parent, id, title, pos, size, style)
{
  m_is_new_instance = (selected_code.IsEmpty()?true:false);

  panel = new wxPanel(this, wxID_ANY);
  title_sz_staticbox = new wxStaticBox(panel, wxID_ANY, wxT("Title"));
  desc_sz_staticbox = new wxStaticBox(panel, wxID_ANY, wxT("Desc"));
  date_sz_staticbox = new wxStaticBox(panel, wxID_ANY, wxT("Date"));
  m_datePicker1 = new wxDatePickerCtrl( panel, wxID_ANY, wxDefaultDateTime, wxDefaultPosition, wxDefaultSize, wxDP_DEFAULT );
  txtTitle = new wxTextCtrl(panel, wxID_ANY, wxEmptyString);
  txtDesc = new wxTextCtrl(panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);
  btnOK = new wxButton(panel, wxID_OK, wxT("OK"));
  btnCancel = new wxButton(panel, wxID_CANCEL, wxT("Cancel"));

  set_properties();
  do_layout();

  if (!m_is_new_instance) {
    do_load_data(selected_code);
  }
}


void TimeLineDetail::set_properties()
{
  txtTitle->SetMinSize( wxSize( 200,-1 ) );
  txtDesc->SetMinSize( wxSize( 200,120 ) );
  if (m_is_new_instance) {
    m_datePicker1->SetFocus();
  } else {
    m_datePicker1->Enable(false);
    txtDesc->SetFocus();
  }
  txtTitle->SetMaxLength(255);
  txtDesc->SetMaxLength(4096);
}


void TimeLineDetail::do_layout()
{
  wxBoxSizer* top_sz = new wxBoxSizer(wxVERTICAL);
  wxBoxSizer* panel_sz = new wxBoxSizer(wxVERTICAL);
  wxGridSizer* buttons_sz = new wxGridSizer(2, 2, 5, 10);
  wxStaticBoxSizer* desc_sz = new wxStaticBoxSizer(desc_sz_staticbox, wxVERTICAL);
  wxStaticBoxSizer* title_sz = new wxStaticBoxSizer(title_sz_staticbox, wxVERTICAL);
  wxStaticBoxSizer* date_sz = new wxStaticBoxSizer(date_sz_staticbox, wxVERTICAL);
  date_sz->Add(m_datePicker1, 0, wxALL, 5);
  panel_sz->Add(date_sz, 0, 0, 0);
  title_sz->Add(txtTitle, 0, 0, 0);
  panel_sz->Add(title_sz, 0, 0, 0);
  desc_sz->Add(txtDesc, 0, 0, -1);
  panel_sz->Add(desc_sz, 0, 0, 0);
  buttons_sz->Add(btnOK, 0, 0, 0);
  buttons_sz->Add(btnCancel, 0, 0, 0);
  panel_sz->Add(buttons_sz, 0, wxALIGN_CENTER_HORIZONTAL, 0);
  panel->SetSizer(panel_sz);
  top_sz->Add(panel, 0, 0, 0);
  SetSizer(top_sz);
  top_sz->Fit(this);
  Layout();
}

void TimeLineDetail::do_load_data(wxString selectedCode)
{
  wxDateTime datetime;
  char const *date_str, *title, *description;
  Buffer *query;
  bool flag;

  query = buffer_alloc(64);

  try {

    buffer_append_string(query, "SELECT date(date), title, description FROM time_lines where id=");
    buffer_append_string(query, selectedCode.char_str());
    buffer_append_string(query, ";");
    buffer_append_space(query, 1);
    sqlite3pp::query qry(db, (const char *)buffer_ptr(query));

    for (sqlite3pp::query::iterator i = qry.begin(); i != qry.end(); ++i) {
      boost::tie(date_str, title, description) = (*i).get_columns<char const*, char const*, char const*>(0, 1, 2);
      txtTitle->SetValue(wxString::FromUTF8(title));
      txtDesc->SetValue(wxString::FromUTF8(description));
      flag = datetime.ParseFormat(wxString::FromAscii(date_str), _T("%Y-%m-%d"));
      if (!flag) {
        wxLogError(_T("You must enter a valid date YYYY-MM-DD!"));
      } else {
        m_datePicker1->SetValue(datetime);
      }
    }
  }
  catch (exception& ex) {
    cout << ex.what() << endl;
  }
  buffer_free(query);
  btnOK->Connect( wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED,
                  wxCommandEventHandler(TimeLineDetail::OnOk));

}

void TimeLineDetail::OnOk(wxCommandEvent& event)
{
  if ( wxMessageBox(wxString::FromUTF8("Sure, about saving?"),
                    wxString::FromUTF8("validation"),
                    wxICON_QUESTION | wxYES_NO, this) == wxYES  )
    {
      event.Skip();
    }

}

TimeLineDetail.h

#include <wx/wx.h>
#include <wx/image.h>
#include <wx/datectrl.h>

#ifndef TIMELINEDETAIL_H
#define TIMELINEDETAIL_H

class TimeLineDetail: public wxDialog {
 public:

  TimeLineDetail(wxWindow* parent, int id, const wxString& title, const wxPoint& pos=wxDefaultPosition, const wxSize& size=wxDefaultSize, long style=wxDEFAULT_DIALOG_STYLE, const wxString& selected_code=wxEmptyString);

 private:
  void set_properties();
  void do_layout();
  void do_load_data(wxString selectedCode);
  bool m_is_new_instance;
  void OnOk(wxCommandEvent& event);


 protected:

  wxStaticBox* desc_sz_staticbox;
  wxStaticBox* title_sz_staticbox;
  wxStaticBox* date_sz_staticbox;
  wxButton* btnOK;
  wxButton* btnCancel;
  wxPanel* panel;

 public:
  wxDatePickerCtrl* m_datePicker1;
  wxTextCtrl* txtTitle;
  wxTextCtrl* txtDesc;


};


#endif // TIMELINEDETAIL_H

Ahora le describiremos al cmake como debe ser compilado y linkeado

nuestro timeline CMakeList.txt

cmake_minimum_required(VERSION 2.6.2)
#Name your project here
project(timeline)

# It was noticed that when using MinGW gcc it is essential that 'core' is mentioned before 'base'.
find_package(wxWidgets COMPONENTS core base REQUIRED html adv)

# wxWidgets include (this will do all the magic to configure everything)
include( ${wxWidgets_USE_FILE} )

set(SRCS timeline.cpp TimeLine.cpp TimeLineDetail.cpp ./sqlite3pp/sqlite3pp.cpp ./generic/buffer.c)
INCLUDE_DIRECTORIES(./sqlite3pp ./generic . )

add_executable(timeline ${SRCS})
find_library(SQLITE3_LIBRARY NAMES sqlite3 libsqlite3)

target_link_libraries(timeline ${wxWidgets_LIBRARIES} ${SQLITE3_LIBRARY})

Ahora en la carpeta donde hemos puesto nuestro código ejecutar cmake ./; make.

Mmmm, no habremos olvidado algo?. Por supuesto la base de datos. Nuestra base de datos solo tiene una tabla4.

CREATE TABLE "time_lines" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT
NULL,
"title" varchar(255),
"description" text,
"date" date,
"created_at" datetime,
"updated_at" datetime);

En kipuamutay lo hemos probado en ArchLinux, no debiera haber inconvenientes para probarlo también en wx-cocoa, estamos en el proceso de probarlo en el n800, nos da un problema al momento de ejecutar, que esperamos con la ayuda de nuestros amigos de wxwidgets solucionar pronto. Espero nuestro próximo articulo se escriba pronto. Hasta entonces.


1. Si estas en win32, solo necesitaras tener el archivo de base de datos y la librería de ejecución dinámica del sqlite.

2. Hemos modificando ligeramente, la librería.

3. En este header hemos agregado modificaciones, para que puedan ser llamadas estas funciones desde c++.

4. Si estas en win32, deberás buscar algún GUI para el sqlite, con el GUI podrás crear el archivo de base de datos.

Last change: 10.07.2012 23:49

blog comments powered by Disqus