Currently focused on…

A brief update on what I’ve being doing during last months:

Yes, you have guessed it. Together with some of my colleagues at Igalia, I’m collaborating in the development of the Meego Touch Framework, the (not so) current MeeGo framework for application development. This development is completely open and it’s being performed through its gitorious repository.

Yes, I know QML is here to replace meegotouch as the MeeGo toolkit, but that doesn’t mean that there won’t be Meego Touch based applications anymore. While QML is being polished and getting ready for action, meegotouch is still the framework to use, specially if you need a low level integration with the platform or you want to provide your users with really complex and cool effects. Also, you will probably be able to use Meego Touch components through QML as well 🙂

Expect some posts explaining some of the internals of the library, as it has some cool stuff to show to the world! 😉

On the way to 0.5

It’s been a bit since my last post about siggy. But don’t worry. That only means that I’ve been working hard on it 😉

I’ve made a real lot of changes to the application since version 0.4.2. And I have some more in mind before releasing 0.5. But I wanted to give you an advance of what I’ve been doing and what I plan to do:

  • Things done:
    • Added some performance improvements here and there, mostly related to avoid unnecessary redraws, and modified the hiding of the dialogs. Maybe you have realized that the more expenses you had in a month, the more it took the new expense dialog to hide. That doesn’t happen anymore, and everything runs a bit faster.
    • Added a new feature to the summary section: Evolution. This option uses the expenses graph to show the evolution of your budget and expenses during a period of months. Is has a monthly and accumulated view as the normal graph. Now you can check during with months you spend more money! 😉
    • Source code restructured, making the appearance of the application totally independent of the rest of the code. Thanks to this, creating new views for the application for different platforms is really easy. This is something a normal user won’t notice but I needed it for other goals ;). Also, this was something almost mandatory for someone who likes designing as I do :).
    • Created a desktop version of the application. Yes, siggy now has a desktop version with a view adapted to the desktop environment 🙂
  • Things to do (in a not relevant order):
    • Create a database updater: I’m a slave of the database schema. I have some features in mind that would break the database compatibility with previous versions of the application. This is something I need to fix. I have in mind a program able to update the database schema and migrate existent data when upgrading to a new version.
    • Implement forecasts: I’d like to add the application the capability to tell you how are you going to finish the month according to your current expense rate. Something similar could be done with a period of months.
    • Remove the packaging stuff from the master branch and put it in a different one. Well, you know, code is code, and packaging is packaging 😛
    • Add an rpm packaging branch.
    • Implement the account password protection feature.
    • And last, but not least, create a MeeGo version of the application 🙂

What do you think? Have I been busy or not? 😉
The only bad new is that I’m not planning to create a new package and upload it to garage and those things yet. I’ll probably just create version 0.4.3 in the repository and wait until version 0.5 to create the new package.

Anyway, feel free to get the source code from gitorious and build it yourself. Just launch build.sh and it will create the package for your platform! 🙂

When Grilo met Phonon

You probably know Grilo. It’s a GLib based framework created to ease the task of searching media to application developers. A couple of weeks ago I checked its code a bit and I really liked its plugin based design, and how easy to use it was. And I wanted to test it, of course 🙂

Some of my colleagues had already created some examples with Grilo, like Iago adding it to Totem or José creating a clutter based player. I wanted to do something different, and as I live between the Qt and the Gnome world, I decided to develop a Qt based Grilo application that used Phonon to play the media found by Grilo. If you are interested in the full source, you can clone my public repository at Igalia (http://git.igalia.com/user/magomez/qtgrilo.git).

So, step one: create the UI. I got a bit of inspiration from the grilo-test-ui program available with the Grilo sources, and this was the result:

You can choose the plugin to use from the combobox on the top. When a plugin is selected, the operations supported by the plugin are indicated. In the image, the Youtube plugin supports searching, browsing and metadata operations.
The bottom-left list shows the available media. It can be the result of a search, query or browse operation. Double clicking on a list item will browse it if it’s a container or show its metadata if it’s a media file. If the selected media contains an URL field, it will be played by pressing the Show button.

Step two: connect Grilo and the UI. This is really simple. Just add the plugins configuration to the plugin registry (for example the youtube one):

config = grl_config_new ("grl-youtube", NULL);
grl_config_set_api_key (config, YOUTUBE_KEY);
grl_plugin_registry_add_config (registry, config);

Then ask the registry about the available sources and add them to the plugins combo so the user can select one:

GrlMediaPlugin **sources;

sources = grl_plugin_registry_get_sources(registry,
                                          FALSE);

int i = 0;
while (sources[i]) {
    pluginCombo->
        addItem(grl_metadata_source_get_name(GRL_METADATA_SOURCE(sources[i])),
                qVariantFromValue(sources[i]));
    i++;
}
g_free(sources);

Be careful here, as in order to store non Qt typed pointers in QVariants, you need to declare them first, so Qt knows about those types. In this code, I defined the 2 types I stored into QVariants:

Q_DECLARE_METATYPE(GrlMediaPlugin*)
Q_DECLARE_METATYPE(GrlMedia*)

And that’s it.

Then, when the user selects a source, ask for its supported operations. If browsing is supported, do it to fill the browser list with the root folder contents:

GrlSupportedOps ops;
ops = grl_metadata_source_supported_operations(GRL_METADATA_SOURCE(currentPlugin));

if (ops & GRL_OP_SEARCH) {
        opsString += "  SEARCH";
        searchButton->setEnabled(true);
} else {
        searchButton->setEnabled(false);
}
if (ops & GRL_OP_QUERY) {
        opsString += "  QUERY";
        queryButton->setEnabled(true);
} else {
        queryButton->setEnabled(false);
}
if (ops & GRL_OP_BROWSE) {
        opsString += QString("  BROWSE");
        browseButton->setEnabled(true);
        browse();
} else {
        browseButton->setEnabled(false);
}
if (ops & GRL_OP_METADATA) {
        opsString += "  METADATA";
} else {
}

operationsLabel->setText(opsString);

Step three: implement the browse, search and query operations. Again, this is a simple process once you understand how Grilo works, and the implementation of the three operations is quite similar. Basically, you need to call grl_media_source_search/query/browse. Besides some flags and parameters for the search, you indicate the string to search/query or the container to browse, and the callback operation. This callback is called whenever a result arrives, and inside you must decide whether to launch a new search/query/browse to get the next chunk of results. Of course, besides this, I’ve added the obtained media to the browser list, so the user can interact with it.

As I explained in my last post, a class method can be used as a GObject signal callback, so this is what I did here as well. When callback method is called, it receives the class instance as the user_data parameter, so from inside the class method I can call the instance method I need. As an example, these are the 3 functions that implement the search operation: search() is the one called to start the operation, searchFinishedCB is the result callback, and searchMethod is the instance methos that adds the result to the browser and launches the search again if needed.

void TestWindow::search()
{
        cancelCurrentOperation();
        clearBrowser();

        string = g_strdup(searchEdit->text().toLatin1());
        currentOpId = grl_media_source_search(GRL_MEDIA_SOURCE(currentPlugin),
                                              string,
                                              grl_metadata_key_list_new(GRL_METADATA_KEY_ID,
                                                                        GRL_METADATA_KEY_TITLE,
                                                                        GRL_METADATA_KEY_CHILDCOUNT,
                                                                        NULL),
                                              0,
                                              BROWSE_CHUNK_SIZE,
                                              (GrlMetadataResolutionFlags)BROWSE_FLAGS,
                                              searchFinishedCB,
                                              this);

}

void TestWindow::searchFinishedCB(GrlMediaSource *source,
                                  guint search_id,
                                  GrlMedia *media,
                                  guint remaining,
                                  gpointer user_data,
                                  const GError *error)
{
        if (!error && media) {
                TestWindow *win = (TestWindow*)user_data;
                win->searchFinished(search_id, media, remaining);
        }

}
void TestWindow::searchFinished(guint search_id,
                                GrlMedia *media,
                                guint remaining)
{
        QString name(grl_media_get_title(media));
        QStandardItem *item = new QStandardItem();
        if (GRL_IS_MEDIA_BOX(media)) {
                QFont font;
                font.setBold(true);
                item->setFont(font);
                gint children = grl_media_box_get_childcount(GRL_MEDIA_BOX(media));
                if (children == GRL_METADATA_KEY_CHILDCOUNT_UNKNOWN) {
                        name += QString(" (?)");
                } else {
                        name += QString(" (%1)").arg(children);
                }
        }
        item->setText(name);
        item->setData(qVariantFromValue(media));
        item->setEditable(false);
        browseModel->appendRow(item);
        operationResults++;

        if (remaining == 0) {
                operationOffset += operationResults;
                if (operationResults >= BROWSE_CHUNK_SIZE &&
                    operationOffset < BROWSE_MAX_COUNT) {
                        operationResults = 0;
                        /* relaunch search */
                        currentOpId = 
                                grl_media_source_search(GRL_MEDIA_SOURCE(currentPlugin),
                                                        string,
                                                        grl_metadata_key_list_new(GRL_METADATA_KEY_ID,
                                                                                  GRL_METADATA_KEY_TITLE,
                                                                                  GRL_METADATA_KEY_CHILDCOUNT,
                                                                                  NULL),
                                                        operationOffset,
                                                        BROWSE_CHUNK_SIZE,
                                                        (GrlMetadataResolutionFlags)BROWSE_FLAGS,
                                                        searchFinishedCB,
                                                        this);
                }
        }
}

After implementing the search, query and browse operations, I implemented the metadata operation as well. So when the user selects and element in the browser, its metadata is retrieved and shown. Its implementation is quite similar to the browse/query/search operations as well, but it doesn’t need to be relaunched as them.

So, the next and final step was playing the media. I started with videos and audio, as using Phonon it was really easy:

void TestWindow::playVideo()
{
        Phonon::VideoWidget *videoWidget = new Phonon::VideoWidget();
        videoWidget->setAttribute(Qt::WA_DeleteOnClose);

        Phonon::MediaObject *mediaObject = new Phonon::MediaObject(videoWidget);
        mediaObject->setCurrentSource(Phonon::MediaSource(QUrl(QString::fromUtf8(grl_media_get_url(currentMedia)))));
        Phonon::AudioOutput *audioOutput = new Phonon::AudioOutput(Phonon::VideoCategory, videoWidget);
        Phonon::createPath(mediaObject, audioOutput);
        Phonon::createPath(mediaObject, videoWidget);
        videoWidget->show();
        mediaObject->play();
}

void TestWindow::playAudio()
{
        QMessageBox msgBox;
        msgBox.setText(QString("Playing %1").arg(grl_media_get_title(currentMedia)));

        Phonon::MediaObject *mediaObject = new Phonon::MediaObject(&msgBox);
        mediaObject->setCurrentSource(Phonon::MediaSource(QUrl(QString::fromUtf8(grl_media_get_url(currentMedia)))));
        Phonon::AudioOutput *audioOutput = new Phonon::AudioOutput(Phonon::MusicCategory, &msgBox);
        createPath(mediaObject, audioOutput);
        mediaObject->play();
        msgBox.exec();
}

Understanding how Phonon works is quite easy when you have learned to use GStreamer first, as the concepts are almost the same (despite Phonon is far easier to use). Basically you need to create a MediaObject and tell it where to get the data. Then create an AudioOutput for the audio, a VideoWidget for the video, connect them and then set the playing state. I was a bit short of time to implement a more featured player, but I wanted to provide a way to stop the playback once started, so it’s stopped when the output window (if it’s a video) or the dialog with the title (if it’s an audio file) are closed.

And finally, the image files. Opening them when they were local files is trivial. But when they are remote ones, you need to download them first. In order to do so, you to use a QNetworkAccessManager, which is the class in charge of the network access. Just use its get() to make a request, and it will notify with a signal when the data has arrived, as you can see in the code:

netManager = new QNetworkAccessManager(this);
connect(netManager, SIGNAL(finished(QNetworkReply*)),
                this, SLOT(netRequestFinished(QNetworkReply*)));

...

void TestWindow::playImage()
{
        QUrl url(QString::fromUtf8(grl_media_get_url(currentMedia)));

        if (url.scheme() == "file") {
                QScrollArea *area= new QScrollArea();
                area->setAttribute(Qt::WA_DeleteOnClose);
                QLabel *label = new QLabel();
                label->setPixmap(QPixmap(url.path()));
                area->setWidget(label);
                area->show();
        } else {
                netManager->get(QNetworkRequest(url));
        }
}

...

void TestWindow::netRequestFinished(QNetworkReply *reply)
{
        QScrollArea *area= new QScrollArea();
        area->setAttribute(Qt::WA_DeleteOnClose);
        QLabel *label = new QLabel();
        QPixmap pix;
        pix.loadFromData(reply->readAll());
        label->setPixmap(pix);
        area->setWidget(label);
        area->show();
        reply->deleteLater();
}

And that’s all… well, almost all… for some reason I haven’t found yet, opening youtube videos is not working, despite I’ve checked the URLs and the videos work if they are atored in the computer… I might be a bug with Phonon but I haven’t found it yet…

As you can see, using Grilo is really easy… even if you decide to mix it with Qt! 🙂

A camera using GDigicam and Qt

You may (or not 😉 ) know that GDigicam is an open source library used in Maemo 5 as a middleware betweeen the camera application and the GStreamer stuff. The goal of the library is to ease the development of camera style applications by hiding the low level stuff to the UI developers, and it allows different GStreamer pipelines to be used in the lower layers to achieve the camera funcionality. Currently the camerabin plugin is the fully supported one, and it’s also the one being used for the N900 camera.

I had the chance to collaborate in the development of the GDigicam library time ago, and it’s currently maintained by some of my colleagues at Igalia. One of them asked me some days ago about the possibility of using GDigicam together with Qt to develop a camera application. You know how this works… that seed was enough to awake my curiosity, so I started working on it 🙂

Some tests later, I’ve developed an experiment integrating both things. A Qt application that uses GDigicam to display a viewfinder in a Qt window, and to get pictures and show a preview of them. I must confess that I got really surprised because it was a really simple task. You can clone my personal repository (http://git.igalia.com/user/magomez/qtcamera.git) if you want to check the code. These are the steps I followed and the tricky parts of the code 🙂

First, extend the QWidget to create a CamWindow widget. In the CamWindow constructor, create the GDigicamManager, the GStreamer camerabin plugin, the GDigicamDescriptor and fill the bin capabilities (this is done in the setupCamerabin method). Connect the callbacks to the GDigicam desired signals and set the initial configuration. In the example, I used CamWindow’s static methods as callbacks of the GDigicam signals. This is fine as long as you don’t need the CamWindow instance. If you need it, then you’ll have to set it as the callback user_data parameter when connecting the signal (as in the preview signal case).

void CamWindow::setupGDigicam()
{
    GstElement *bin;
    GDigicamDescriptor *descriptor = NULL;

    /* create the GDigicamManager */
    manager = g_digicam_manager_new();
    colorkey = 0;

    /* create the bin */
    bin = g_digicam_camerabin_element_new ("v4l2camsrc",
                                           "dspmp4venc",
                                           "hantromp4mux",
                                           "pulsesrc",
                                           "nokiaaacenc",
                                           "jpegenc",
                                           NULL,
                                           "xvimagesink",
                                           &colorkey);

    /* create and fill the descriptor*/
    descriptor = g_digicam_camerabin_descriptor_new (bin);
    descriptor->max_zoom_macro_enabled = 6;
    descriptor->max_zoom_macro_disabled = 6;
    descriptor->max_digital_zoom = 6;


..... more descriptor capabilities stuff....


    /* set the bin and the descriptor to the manager */
    g_digicam_manager_set_gstreamer_bin (manager,
                                         bin,
                                         descriptor,
                                         NULL);

    /* connect to the manager's signals */
    g_signal_connect (manager, "pict-done",
                     (GCallback) captureDone,
                      NULL);
    g_signal_connect (manager, "capture-start",
                     (GCallback) captureStart,
                      NULL);
    g_signal_connect (manager, "capture-end",
                     (GCallback) captureEnd,
                      NULL);
    g_signal_connect (manager, "image-preview",
                     (GCallback) imagePreview,
                      this);

    /* set initial configuration */
    setOperationMode(G_DIGICAM_MODE_STILL);
    setResolution(G_DIGICAM_RESOLUTION_HIGH,
                  G_DIGICAM_ASPECTRATIO_16X9);
    setFlashMode(G_DIGICAM_FLASHMODE_AUTO);
    enablePreview(true);
}

I’ve created some private members in CamWindow to encapsulate the GDigicam function calls. They are used for initialization and also called through the options in the window menu (change resolution, play/stop bin) and when clicking the capture button. This is the one to set the resolution for example:

void CamWindow::setResolution(GDigicamResolution res,
                              GDigicamAspectratio ar)
{
    GDigicamCamerabinAspectRatioResolutionHelper *helper = NULL;

    helper = g_slice_new (GDigicamCamerabinAspectRatioResolutionHelper);
    helper->resolution = res;
    helper->aspect_ratio = ar;

    g_digicam_manager_set_aspect_ratio_resolution (manager,
                                                   helper->aspect_ratio,
                                                   helper->resolution,
                                                   NULL,
                                                   helper);

    g_slice_free (GDigicamCamerabinAspectRatioResolutionHelper, helper);
}

One of the capabilities of the camerabin is that through the use of a colorkey, it allows blending UI components over the video stream. To make this work, the background of the window where the video is put must be filled wit the colorley color (this color is provided when creating the camerabin element). This is why this is done in the paintEvent method of the window:

void CamWindow::paintEvent (QPaintEvent *)
{
    QPainter painter(this);
    QBrush brush;

    QColor color((colorkey & 0x00ff0000) >> 16,
                 (colorkey & 0x0000ff00) >> 8,
                  colorkey & 0x000000ff);

    painter.save();
    painter.fillRect(0, 0, width(), height(), color);
    painter.restore ();
}

This done, by calling g_digicam_manager_play_bin() and g_digicam_manager_stop_bin(), you can control the viewfinder running on the window. The g_digicam_manager_play_bin() function received the XWindow id of the widget, which is obtained through the winId() member of QWidget.

Finally, when the pipeline is running, by calling g_digicam_camerabin_get_still_picture(), you can capture a picture. When doing so, besides the picture being stored in the provided path, the manager will emit (if preview is active) a signal containing a preview of the captured picture. In order to show this preview, I connected the imagePreview member of CamWindow to the preview signal. Inside this method, the GdkPixbuf received is turned into a QImage and then a QPixmap that is shown in a new window:

void CamWindow::imagePreview(GDigicamManager *manager,
                             GdkPixbuf *preview,
                             gpointer data)
{
    QLabel *label = new QLabel();
    label->setAttribute(Qt::WA_DeleteOnClose);
    label->setWindowFlags(label->windowFlags() | Qt::Window);

    QImage image(gdk_pixbuf_get_pixels(preview),
                 gdk_pixbuf_get_width(preview),
                 gdk_pixbuf_get_height(preview),
                 gdk_pixbuf_get_rowstride(preview),
                 QImage::Format_RGB888);

   QPixmap pixmap(QPixmap::fromImage(image));
                  label->setPixmap(pixmap);
                  label->show();
}

If you get the code and run the example in an N900, you will see a window with a capture button. Go to the menu, select play and the viewfinder will show up (and capture button will be over it!). You can change the resolution of the picture through the menu, and press the capture button to get a picture, and see the preview of the capture in a new window. For the moment, those are the features I’ve implemented in the example.

It’s quite simple, isn’t it? 🙂

Vagalume dressed for the Moblin party

Something I’ve been playing with that I’ve not mentioned before was my experiments adapting Vagalume to the Moblin look and feel.

As you know, Moblin’s UI is (should I say was???) Clutter based, with lots of fancy transitions. Also, it uses fullscreen windows in a one-window-per-desktop approach, and there aren’t dropdown menus neither dialogs.

And as you also know, Vagalume has a non fullscreen Gtk+ based window. And it uses dropdown menus and dialogs.

So, this needed a bit of love 🙂

I started by adding some fancy effects to the UI by using Clutter. I did some modifications to the Vagalume controller so I could get more than one cover at a time and put them into a GtkClutterEmbed, with a rotation effect when switching from a song to the next one. I shouldn’t say it, but the result was quite cool 😛 :

And then started with the menu. I removed all the dropdown stuff and replaced it a side panel. This panel can stay hidden or shown.

When some of the options in the panel is clicked, is expanded, hiding the cover list widget, and showing the different suboptions, as you can see in the image:

And finally, I added to the cover list widget the capability to show error messages, so no dialogs were needed to do it, and put the window in fullscreen mode. I think the result integrates pretty well, don’y you think? 😉

I uploaded the package to the Intel AppStore so it is (or should be at some point) available. But if you want to play a bit, you can clone my repo at the Igalia (http://git.igalia.com/user/magomez/vagalume-moblin.git) and check the moblin branch.

Hope you like it! 🙂

siggy now on extras-testing

As you know, PR1.2 is there, so I’ve retaken siggy to have it ready to use.

Finally all the Qt 4.6 dependency problems have gone and the promotion to extras-testing became available. But I was missing a little detail, as I had no bugtracker link in my debian/control file. So I’ve released a new minor version (0.4.1) containing that link, uploaded all the stuff as usual (gitorious, garage), and promoted it to extras-devel.

So, help me a bit with the testing and I hope it will be soon available at the extras repository!! 🙂

The piggy keeps growing: 0.4 is here!

It took me longer than expected, but at last I’ve been able to release the version 0.4 of siggy. As usual, you can find the source code at gitorious, and together with the armel package at Maemo garage, and I’ve also uploaded the package to extras-devel.

For those who haven’t realized, due to the upcoming PR1.2, the Maemo 5 sdk has been updated, so developers can perform the appropriate changes to their apps in order to have them ready for it. To the Qt developers this means that the brand new Qt 4.6 has replaced the old 4.5 version, so we don’t need to keep using the previous testing packages anymore. I’ve modified the application to use the new libs and everything seems to be fine… despite I haven’t been able to promote it to extras-testing yet… it seems that there is a dependency problem somewhere that I need to check. I also need to ask for a bugzilla component at the Maemo bugzilla. But don’t panic ;), I’ll work on it 🙂

So, what’s new in this release besides the change in the dependencies? I guess the main change is the multiple account support. Now you can create more than one account in the application (personal, job, whatever) and assign the expenses to it.
How does this work? When users open the application, they can’t do anything else than managing items and accounts. Any other action is forbidden (and not shown in the menu):

At this point, users need to create an account and select it to be able to anything. Now each account has its own default budget:

And after creating the account, users may update, delete or select it:

Once the account is selected, the application behaves exactly as before, but all the data stored will belong only to that account (budget, expenses and scheduled expenses), and the active account is shown in the application’s title bar.

A small difference is that the Budget option in the menu doesn’t allow to set a default budget as before (this is achieved by editing the account), but only changes the budget of the current month.

At any point, users may select any other account to work with, or delete stored accounts. In the delete case, the user will be prompted for confirmation, as deleting an account will also delete all the data associated to that account. An small constraint is that the active account can’t be deleted.

Keep in mind that, due to this new feature, this version is not database compatible with the previous one so, if you had a previous version installed, you will need to delete the old database to use this new version. To do so, you can remove the folder $HOME/.siggy or you can purge the application.

Is this what you expected? Do you think of any improvement that can be made? Feel free to raise your had! 🙂

Besides that main change, there are a couple of minor ones. The first one is that when adding/updating an expenses, the days in the selector have been humanized, so they show something like “Mon, 1″… “Tue, 2″… Yesterday… Today… Tomorrow… which is easier to understand. Thanks to Joaquim Rocha for this patch!. Also, as promised, I’ve added an UUID column to the expenses table in the database, thinking of a possible synchronization service (this was really easy when I found the QUuid class).

And last, but not least, I’ve been working in the password protection issue, but I’m currently hesitating about how to implement it. I could add an optional password to protect the whole app, so it doesn’t even launch without entering the password, or I could add an optional password to each account, so some of the accounts could be used without entering it and some others will require the password. What do you think?
For the moment, in this version I’ve added a password column to the account table that would allow the second option (probably the chosen one), but I haven’t implemented the feature as I didn’t have too much time lately. But at least the field in the database is already there, so no database changes are needed later 🙂

And I guess this is all for 0.4. Hope you like it! 🙂

siggy version 0.3 released!

As promised, the brand new 0.3 version of siggy is out 🙂
You can get the source code and package at garage, and it has also been uploaded to the extras-devel repository. You can check the source code from its git repository as well.

When developing this version, my idea was to add some way to get information from the data stored in the database. So I thought of two views. One of them would be a statistical view, where I could check how I’ve been evolving during a period of time, showing totals, average values, etc.
The other view would be a kind of chart representing the amount of money spent during a period in each of the items defined in the application.

Some hours coding and this is the result:

What do you think? Could this be useful to you? 🙂

Unfortunately, the next mayor update is not out yet, so the limitation to move the package from extras-devel to extras-testing is still there. Anyway, this can be seen as an advantage 😉
For the upcoming 0.4 version, I have planned a couple of features requested by you:

  • The first one is adding support multiple accounts, just in case someone wants to handle more than one of them.
  • The second one is adding to the expenses in the database some kind of unique id that would allow synchronisation with a centralized service/database. This one opens a new and really interesting functionality for the application: it could be used by workers from a company to write down their work expenses. Periodically, the workers might sinchronize their expenses with the central server, so they are automatically queued to be paid as allowances. Sounds cool, doesn’t it? 🙂
  • If I have enough time, I will also add password protection to open the application

So, why the delay in the mayor update is an advantage? All these new features require changes in the database tables, which means problems for those who have started to use the application using a previous vesion. So, my advice is: test the application as much as you can, report bugs, patches, translations, etc, but don’t start to use it regularly yet. Wait a bit more. I’ll try to have the next version ready before the upcoming mayor update. This way, when the update is out, you’ll be able to install the new version without migration problems 🙂
Anyway, if there’s someone who wants to start using it asap, just tell me, and I’ll provide a script to update the database together with the new version.

I’m eager to get feedback from you!! 🙂

(Update: I put https URLs instead of http. Sorry! Should be fixed now.)

… and the piggy got detained in the border…

… suspicious of carrying illegal dependencies XD

You may know form my last post, that I’ve released the 0.2 version of siggy. During these days I’ve been performing all the paperwork to put available to the world: creating the project at garage.maemo, creating the packages so you could download them, and also uploading them to the extras-devel repository.

After doing so, I was happily going to promote the package to extras-testing, when the border guard came and stopped me: the piggy may not go beyond here, son, he said. What does this mean? Easy: siggy depends on the 4.6 Qt libraries (to be ble to use the Maemo5 widgets). These libraries are been developed and available from extras-devel, but with different package names than the 4.5 ones (libqt4-maemo5-* instead of libqt4-*), and different installation path. This was done so the users could test the new version on the libraries without mixing them with the system 4.5 ones. But at some point, these packages will be removed from extras-devel and they will replace the 4.5 ones (this is planned for the next major update AFAIK). So, as siggy depends on those about to dissapear libqt4-maemo5-* libraries, once the next update was released, it would stop working.

I must say that I was already aware about this, and I had planned creating a new release with the new libraries as soon as the update was available, but I guess the policy of non promoting the applications to extras-testing makes sense as well 🙂
(BTW, good work to Qt team! keep up with it!) 🙂

So, what’s the plan now? Well, current package (both at garage and extras-devel) will work with the libqt4-maemo5-* libraries available in extras devel. So you can grab them and start using the application. But if don’t want to mess with your system and install these libraries, you just need to wait for a while. At some point, the next update will be released, and the Qt 4.6 libraries will be installed in your system. At that moment I’ll create a new package with the new dependencies (you can do it also from the source code), and will update it in extras-devel and extras-testing, so you can easily install it 🙂

In the meanwhile, working in the 0.3 release, I’m currently developing a summary window where the user would be able to get some information from a period of time: amount of money spent and saved, average month expenses, average money saved, etc, together with a pie chart showing the percentage of money spent in each item.

After that, I have some cool suggestions coming form you that are really interesting but… hey!… that’s another story 😉