Меnu:


Quick App with wxwidgets

From our time as Palm developers, we missed the great calendar application that Palm used to provide us, as the global search feature too . Sometime ago We did something similar with ror. The timeline on ror was very easy to do, the only annoyance we found was having to start the app every time we need to register a new event on the ror timeline. We were in the urgency to find something more comfortable to use.

Years has passed quickly, now we have wxwidgets as part of our tooling. So why not give the timeline a try on wxwidgets. Our goal is to put the timeline on the n800 and having again it at hand (literally) when needed.

Requirements

At kipuamutay our development host is Archlinux, but following the steps to recreate the timeline is going to be very easy on any other distribution. No time for trying it on win32. But let me know how it goes if you follow that way. Those are the packages and libraries we are going to use:

TimeLine

Our timeline app has a frame an a dialog (using wxwidgets terminology), our frame is going to display an event list, and a search box for filtering the events, on the dialog we are going to edit the events, and add new events.

Hands at work (or fingers)

Defining our app:

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();
}


Giving a quick view to our timeline app, put the focus on the include lines, we realize we are going to need sqlite3pp, after downloading it, uncompress it within the directory our code resides. As we move forward we are going to add TimeLine.h, right now it is time to add a c library that is going to easy us the work with strings when doing database queries buffer (free to use) (put it on folder named generic). The version of buffer we provide is a slightly modified version from the original buffer. If the reader wants to inspect our changes, go ahead download and compare..
/* $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;

  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 */

As we already have mentioned

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

After finishing the frame. It is the dialog time

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

CMake input file for compilation and linkage

Out 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})

On the source code directory run cmake ./; make.

Ops, Have We missed something?. Right the database. Our database has just one table4.

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

At kipuamutay we have tested the timeline on ArchLinux, it should be very easy to try it on wx-cocoa, we are near to test in on the n800, It is not running at the moment, yet we are waiting our wxwidgets friends answer our question to try it again. As soon as we reach the goal we are going to update you. Hoping timeline makes a difference for your digital organization.


1. On win32, It is necessary the database file and the sqlite dynamic library.

2. The Buffer library is a slightly modified version.

3. It was necessary to modify the header for calling the functions from c++.

4. On win32, look for a sqlite GUI, for getting the database file.

Last change: 10.07.2012 23:06

blog comments powered by Disqus