Added a Notify class
[shopper.git] / src / ui / mainwindow.cc
blob60b1dc562c7a4eba04fd668db4d7bdacab5b2ed5
1 /* Shopper
2 * Copyright (C) 2008 David Greaves
4 * This software is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public License
6 * as published by the Free Software Foundation; either version 2.1 of
7 * the License, or (at your option) any later version.
9 * This software is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this software; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 * 02110-1301 USA
21 #define DEBUG_SHOPPER 1
22 #include "shopper.h" // automake, i8n, gettext
24 #include "mainwindow.h"
25 #include "CatListModel.h"
26 #include "CategoryDialog.h"
27 #include "ItemDialog.h"
28 #include "notify.h"
30 // Global variable
31 Shopper::Notify *notify;
33 // class MainWindow : public QMainWindow
34 MainWindow::MainWindow(QString file) :
35 mylist(NULL),
36 lv(NULL),
37 filename(file),
38 full_screen(false),
39 has_rotation(false)
41 init_MainWindow(NULL);
43 MainWindow::MainWindow(Shopper::List &l) :
44 mylist(NULL),
45 lv(NULL),
46 filename(""),
47 full_screen(false),
48 has_rotation(false)
50 init_MainWindow(&l);
53 void MainWindow::init_MainWindow(Shopper::List *l)
55 // The constructor handles only 'one off' stuff - main menu,
56 // buttons etc. Anything List specific is seperated off to
57 // handle_list() so it can be reused when opening new lists
59 // xrandr detection from adv-backlight...
60 #ifdef XRANDR
61 Display *dpy;
62 int xrr_event_base, xrr_error_base;
63 int xrr_major, xrr_minor;
64 dpy = XOpenDisplay (NULL);
65 if (dpy == NULL)
67 DEBUG("Couldn't open display\n");
68 has_rotation = false;
69 } else {
70 if (XRRQueryExtension (dpy, &xrr_event_base, &xrr_error_base) == false
71 || XRRQueryVersion (dpy, &xrr_major, &xrr_minor) == 0 || xrr_major != 1 || xrr_minor < 1)
72 has_rotation = false;
73 else
74 has_rotation = true;
75 XCloseDisplay (dpy);
77 #endif
78 // Actions
79 // UI components
80 QAction *addItemAct = new QAction(QIcon(":/images/new.png"), tr("Add Item"), this);
81 addItemAct->setStatusTip(tr("Add an item to the list"));
82 connect(addItemAct, SIGNAL(triggered()), this, SLOT(on_action_add_item()));
84 QAction *manageCatAct = new QAction(QIcon(":/images/new.png"), tr("Manage Categories"), this);
85 manageCatAct->setStatusTip(tr("Manage the list of categories"));
86 connect(manageCatAct, SIGNAL(triggered()), this, SLOT(on_action_manage_category()));
88 QAction *clearWantedAct = new QAction(QIcon(":/images/new.png"), tr("Clear Wanted"), this);
89 clearWantedAct->setStatusTip(tr("Clear the items marked as 'wanted'"));
90 connect(clearWantedAct, SIGNAL(triggered()), this, SLOT(on_action_clear_wanted()));
92 QAction *emptyBasketAct = new QAction(QIcon(":/images/new.png"), tr("Empty Basket"), this);
93 emptyBasketAct->setStatusTip(tr("Clear the items marked as 'in the basket'"));
94 connect(emptyBasketAct, SIGNAL(triggered()), this, SLOT(on_action_clear_bought()));
96 QAction *fullListAct = new QAction(QIcon(":/images/new.png"), tr("Out Shopping: Full List"), this);
97 fullListAct->setStatusTip(tr("Show the full list (items in the basket will be visible and ticked)"));
98 connect(fullListAct, SIGNAL(triggered()), this, SLOT(on_action_fullList()));
100 QAction *whatsLeftAct = new QAction(QIcon(":/images/new.png"), tr("Out Shopping: What's Left"), this);
101 whatsLeftAct->setStatusTip(tr("Show what's left to get"));
102 connect(whatsLeftAct, SIGNAL(triggered()), this, SLOT(on_action_whatsLeft()));
104 QAction *makingListAct = new QAction(QIcon(":/images/new.png"), tr("Making List"), this);
105 makingListAct->setStatusTip(tr("Pick items to get when you go shopping"));
106 connect(makingListAct, SIGNAL(triggered()), this, SLOT(on_action_makingList()));
108 QAction *newAct = new QAction(QIcon(":/images/new.png"), tr("Add Item"), this);
109 newAct->setStatusTip(tr("Add an item to the list"));
110 connect(newAct, SIGNAL(triggered()), this, SLOT(on_action_file_new()));
112 QAction *openAct = new QAction(QIcon(":/images/new.png"), tr("Open List"), this);
113 openAct->setStatusTip(tr("Open a new list"));
114 connect(openAct, SIGNAL(triggered()), this, SLOT(on_action_file_open()));
116 QAction *saveAct = new QAction(QIcon(":/images/new.png"), tr("Save List"), this);
117 saveAct->setStatusTip(tr("Save the list"));
118 connect(saveAct, SIGNAL(triggered()), this, SLOT(on_action_file_save()));
120 QAction *saveAsAct = new QAction(QIcon(":/images/new.png"), tr("Save List As..."), this);
121 saveAsAct->setStatusTip(tr("Save the list with a new name"));
122 connect(saveAsAct, SIGNAL(triggered()), this, SLOT(on_action_file_saveas()));
124 QAction *quitAct = new QAction(QIcon(":/images/new.png"), tr("Quit"), this);
125 quitAct->setStatusTip(tr("Finish"));
126 connect(quitAct, SIGNAL(triggered()), this, SLOT(on_action_file_quit()));
128 QAction *aboutAct = new QAction(QIcon(":/images/new.png"), tr("About..."), this);
129 aboutAct->setStatusTip(tr("About Shopper..."));
130 connect(aboutAct, SIGNAL(triggered()), this, SLOT(on_action_about()));
132 // Defined in class to be available elsewhere
133 nextAct = new QAction(QIcon(":/images/new.png"), tr("Next Category"), this);
134 nextAct->setStatusTip(tr("View the next category"));
135 connect(nextAct, SIGNAL(triggered()), this, SLOT(on_action_next()));
137 QAction *prevAct = new QAction(QIcon(":/images/new.png"), tr("Prev Category"), this);
138 prevAct->setStatusTip(tr("View the previous category"));
139 connect(prevAct, SIGNAL(triggered()), this, SLOT(on_action_prev()));
141 // Menus
142 menuBar()->addAction(addItemAct);
143 menuBar()->addAction(manageCatAct);
145 QMenu *clearMenu = menuBar()->addMenu(tr("Clear List"));
146 clearMenu->addAction(clearWantedAct);
147 clearMenu->addAction(emptyBasketAct);
149 QMenu *modeMenu = menuBar()->addMenu(tr("Mode"));
150 modeMenu->addAction(fullListAct);
151 modeMenu->addAction(whatsLeftAct);
152 modeMenu->addAction(makingListAct);
154 menuBar()->addSeparator();
156 QMenu *fileMenu = menuBar()->addMenu(tr("File"));
157 fileMenu->addAction(newAct);
158 fileMenu->addAction(openAct);
159 fileMenu->addAction(saveAct);
160 fileMenu->addAction(saveAsAct);
161 fileMenu->addAction(quitAct);
163 QMenu *helpMenu = menuBar()->addMenu(tr("Help"));
164 helpMenu->addAction(aboutAct);
166 // Toolbar
167 buttonBar = addToolBar(tr("Buttons"));
168 addToolBar(Qt::BottomToolBarArea, buttonBar); // move it to the right area
169 buttonBar->addAction(addItemAct);
170 buttonBar->addAction(manageCatAct);
171 buttonBar->addSeparator();
172 buttonBar->addAction(fullListAct);
173 buttonBar->addAction(whatsLeftAct);
174 buttonBar->addAction(makingListAct);
175 buttonBar->addSeparator();
176 buttonBar->addAction(prevAct);
177 buttonBar->addAction(nextAct);
179 // Status
180 statusBar()->showMessage(tr("Ready"));
182 // Notify is a SystemTrayIcon used for notify messages
183 // Note this is a globally accessible object - nothing to do with MainWin except we parent it
184 notify=new Shopper::Notify(style()->standardIcon(QStyle::SP_FileDialogListView));
185 notify->show();
187 // size and position
188 readSettings();
190 // If we got a filename then read it in...
191 if (l == NULL) {
192 l = Shopper::List::from_file(filename);
193 if (l) { // If filename valid
194 create_list_view(l);
195 } else { // If filename wasn't read
196 on_action_file_new();
199 // Hildon::Banner::show_information(*this, "Created a new shopping list");
200 // while (!mylist) { // mylist and list_view are done in a file_open()
201 // DEBUG("Couldn't open " << filename << " for reading\n");
202 // on_action_file_open();
203 // FIXME :: Popup dialog box and allow new/open
204 // }
206 } else {
207 // Now do List specific stuff
208 create_list_view(l);
210 _LEAVE;
212 void MainWindow::create_list_view(Shopper::List *list)
214 // Remove the old listview... and any data
215 delete mylist;
216 delete lv;
218 // and use a list and a new main shopperList widget
219 mylist = list;
220 lv = new Shopper::ListView(*list);
221 setCentralWidget(lv);
222 // GUI: set title, add the view, update the menu
223 setWindowTitle(mylist->modeText());
224 // Make sure there's a menu to show to enable the popup functionality
226 catCombo = new QComboBox();
227 Shopper::CatListModel *clmodel = new Shopper::CatListModel(*mylist);
228 catCombo->setModel(clmodel);
229 catCombo->setModelColumn(1);
230 catCombo->setCurrentIndex(clmodel->currentIndex());
231 connect(catCombo, SIGNAL(activated(int)),
232 this, SLOT(cat_selected(int)));
233 connect(clmodel, SIGNAL(currentChanged(int)),
234 catCombo, SLOT(setCurrentIndex(int)));
235 buttonBar->insertWidget(nextAct, catCombo);
237 _LEAVE;
240 void MainWindow::cat_selected(int i)
242 _ENTER;
243 QVariant v = catCombo->itemData(catCombo->currentIndex());
244 Shopper::Category *c = v.value<Shopper::Category*>();
245 mylist->make_category_active(*c);
249 void MainWindow::closeEvent(QCloseEvent *event)
251 event->accept();
254 void MainWindow::on_action_about()
256 QMessageBox::about(this, tr("About Application"),
257 tr("The <b>Application</b> example demonstrates how to "
258 "write modern GUI applications using Qt, with a menu bar, "
259 "toolbars, and a status bar."));
263 void MainWindow::readSettings()
265 QSettings settings("dgreaves", "Shopper");
266 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
267 QSize size = settings.value("size", QSize(400, 400)).toSize();
268 resize(size);
269 move(pos);
272 void MainWindow::writeSettings()
274 QSettings settings("dgreaves", "shopper");
275 settings.setValue("pos", pos());
276 settings.setValue("size", size());
279 // Action handlers
280 // Add item
281 void MainWindow::on_action_add_item()
283 Shopper::ItemAdd itemD(this, mylist);
284 if (itemD.exec() == QDialog::Accepted) {
285 DEBUG("\nADDED:\n");
289 void MainWindow::on_action_manage_category()
291 Shopper::CategoryManage catD(this, mylist);
292 catD.exec();
295 // Category cycle
296 void MainWindow::on_action_next(){ mylist->make_next_category_active(); }
297 void MainWindow::on_action_prev(){ mylist->make_prev_category_active(); }
299 // Clear List
300 void MainWindow::on_action_clear_wanted() { clear_list(CLEAR_WANTED); }
301 void MainWindow::on_action_clear_bought() { clear_list(CLEAR_BOUGHT); }
302 void MainWindow::clear_list(clear_list_mode m)
304 for (Shopper::List::pItemIter it = mylist->itemsI(); it != mylist->itemsEnd(); it++) {
305 (*it)->set_bought(false); // Always clear bought
306 if (m == CLEAR_WANTED) {
307 (*it)->set_wanted(false);
310 if (m == CLEAR_WANTED) {
311 set_state(Shopper::MAKING_LIST);
314 // Shopping mode
315 void MainWindow::on_action_fullList() { set_state(Shopper::OUT_SHOPPING); }
316 void MainWindow::on_action_whatsLeft() { set_state(Shopper::WHATS_LEFT); }
317 void MainWindow::on_action_makingList() { set_state(Shopper::MAKING_LIST); }
318 void MainWindow::set_state(Shopper::sState s)
320 Shopper::sState old = mylist->get_state();
321 mylist->set_state(s); // Set the state
322 if (s == Shopper::MAKING_LIST) { // And go to category '1'
323 if (mylist->get_active_category() == NULL) { // show category 1 rather than 'Everything'
324 mylist->make_category_active(**(mylist->categoriesI()));
327 // And go to category 'Everything' if switching to an 'out shopping' state.
328 if (s == Shopper::WHATS_LEFT || s == Shopper::OUT_SHOPPING) {
329 if (old == Shopper::MAKING_LIST) mylist->no_category_active();
331 // set_title(mylist->modeText()); // Gtk
333 ////////////////////////////////////////////////////////////////
334 // File Handling
335 void MainWindow::on_action_file_new()
337 // FIXME: This really ought to check if the user *WANTS* to save...
338 if (mylist)
339 on_action_file_save();
340 filename="";
341 create_list_view(new Shopper::List(
342 QString(
343 "<list name='Supermarket' state='0'>"
344 "<category name='Aisle 1 - Dairy'>"
345 "<item desc='Cheese' note='note, no smelly cheese' wanted='1' bought='0'/>"
346 "<item desc='Bacon' note='Extra needed for Gary&apos;s butties' wanted='1' bought='0'/>"
347 "</category>"
348 "<category name='Frozen'>"
349 "<item desc='Peas' note='' wanted='1' bought='0'/>"
350 "<item desc='Prawns' note='' wanted='1' bought='0'/>"
351 "</category>"
352 "<category name='Veg'>"
353 "<item desc='Potatoes' note='' wanted='0' bought='0'/>"
354 "<item desc='Brocolli' note='' wanted='1' bought='0'/>"
355 "</category>"
356 "</list>"
357 )));
359 void MainWindow::on_action_file_open()
362 // Get a filename
363 Hildon::FileChooserDialog dialog(*this, Gtk::FILE_CHOOSER_ACTION_OPEN);
364 int response = dialog.run();
365 dialog.hide();
367 //Handle the response:
368 if (response == Gtk::RESPONSE_OK) {
369 // Open file and create a view from it.
370 create_list_view(Shopper::List::from_file(filename=dialog.get_filename()));
371 save_filename(filename); // FIXME this should be on success
372 } else {
373 if (mylist == NULL) on_action_file_new(); // If CANCEL pressed and there's no list, make a new one
377 ////////////////////////////////////////////////////////////////
378 void MainWindow::on_action_file_save()
380 DEBUG("Saving....\n");
381 if (filename=="") {
382 on_action_file_saveas();
383 } else {
384 mylist->to_file(filename);
387 void MainWindow::on_action_file_saveas()
390 // Get a filename
391 Hildon::FileChooserDialog dialog(*this, Gtk::FILE_CHOOSER_ACTION_SAVE);
392 if (filename=="") {
393 dialog.set_current_name("ShoppingList");
394 } else {
395 dialog.set_filename(filename);
397 int response = dialog.run();
398 dialog.hide();
399 if (response == Gtk::RESPONSE_OK)
401 QString filename = dialog.get_filename();
402 save_filename(filename);
403 mylist->to_file(filename);
407 void MainWindow::on_action_file_quit()
409 on_action_file_save();
410 // FIXME
411 hide(); //Closes the main window to stop the Gtk::Main::run().
414 bool MainWindow::on_signal_window_closed()
416 on_action_file_quit();
417 return true; // propogate signal so hide() happens
420 bool MainWindow::on_key_pressed()
423 if(!event)
424 return false;
426 switch (event->keyval)
428 case GDK_Up:
429 case GDK_Down:
430 case GDK_Left:
431 case GDK_Right:
432 case GDK_Return:
433 case GDK_Escape:
434 case GDK_F4: // Menu
435 case GDK_F5: // Home
436 break;
437 case GDK_F6: // Fullscreen
438 on_action_fullscreen();
439 return true;
440 case GDK_F7: // Zoom in
441 case GDK_F8: // Zoom out
442 default:
443 break;
446 // Returning true would stop the event now
447 return false;
451 void MainWindow::on_action_fullscreen()
453 if (full_screen){
454 // unfullscreen(); // gtk
455 } else {
456 // fullscreen(); // gtk
458 full_screen = !full_screen;
460 void MainWindow::save_filename(QString name)
464 void MainWindow::on_action_rotate()
466 if (! has_rotation) return;
468 XRRScreenConfiguration *scr_config;
469 // Rotation current_rotation;
470 int size;
471 // int i;
472 Display *dpy;
473 int screen;
475 dpy = XOpenDisplay (NULL);
476 screen = DefaultScreen (dpy);
477 scr_config = XRRGetScreenInfo (dpy, RootWindow (dpy, screen));
478 size = XRRConfigCurrentConfiguration (scr_config, &rotation);
480 if (rotation == RR_Rotate_0) {
481 rotation = RR_Rotate_90;
482 } else {
483 rotation = RR_Rotate_0; // always rotate back to 0
486 XRRSetScreenConfig (dpy, scr_config, RootWindow (dpy, screen), size, rotation, CurrentTime);
487 XRRFreeScreenConfigInfo (scr_config);
488 XCloseDisplay (dpy);
492 void MainWindow::on_action_help()