Move from SLabel to ActiveLabel
[shopper.git] / src / ui / mainwindow.cc
blobc6b94e9fce27971e1744d786a183e73ee2e1b1a6
1 /* Shopper
2 * Copyright (C) 2008 David Greaves <david@dgreaves.com>
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 "mainwindow.h"
23 #include "shopper.h" // automake, i8n, gettext
25 #include "CatListModel.h"
26 #include "CategoryDialog.h"
27 #include "ItemDialog.h"
28 #include "PreferencesDialog.h"
29 #include <QSettings>
31 // class MainWindow : public QMainWindow
32 MainWindow::MainWindow(QString file) :
33 mylist(0),
34 catCombo(0),
35 lv(0),
36 filename(file),
37 fontsize(4),
38 full_screen(false),
39 has_rotation(false)
41 // The constructor handles only 'one off' stuff - main menu,
42 // buttons etc. Anything List specific is separated off to
43 // handle_list() so it can be reused when opening new lists
45 // xrandr detection from adv-backlight...
46 #ifdef XRANDR
47 Display *dpy;
48 int xrr_event_base, xrr_error_base;
49 int xrr_major, xrr_minor;
50 dpy = XOpenDisplay (0);
51 if (dpy == 0)
53 DEBUG("Couldn't open display\n");
54 has_rotation = false;
55 } else {
56 if (XRRQueryExtension (dpy, &xrr_event_base, &xrr_error_base) == false
57 || XRRQueryVersion (dpy, &xrr_major, &xrr_minor) == 0 || xrr_major != 1 || xrr_minor < 1) {
58 DEBUG("Display has no rotation\n");
59 has_rotation = false;
60 } else {
61 DEBUG("Display has rotation\n");
62 has_rotation = true;
64 XCloseDisplay (dpy);
66 #endif
67 // Actions
68 // UI components
69 QAction *addItemAct = new QAction(QIcon("/usr/share/icons/hicolor/64x64/apps/shopper/add.png"),
70 tr("Add Item"), this);
71 addItemAct->setStatusTip(tr("Add an item to the list"));
72 connect(addItemAct, SIGNAL(triggered()), this, SLOT(on_action_add_item()));
74 QAction *manageCatAct = new QAction(QIcon("/usr/share/icons/hicolor/64x64/apps/shopper/categories.png"),
75 tr("Manage Categories"), this);
76 manageCatAct->setStatusTip(tr("Manage the list of categories"));
77 connect(manageCatAct, SIGNAL(triggered()), this, SLOT(on_action_manage_category()));
79 QAction *clearWantedAct = new QAction(qApp->style()->standardIcon(QStyle::SP_FileDialogListView),
80 tr("Clear Wanted"), this);
81 clearWantedAct->setStatusTip(tr("Clear the items marked as 'wanted'"));
82 connect(clearWantedAct, SIGNAL(triggered()), this, SLOT(on_action_clear_wanted()));
84 QAction *emptyBasketAct = new QAction(qApp->style()->standardIcon(QStyle::SP_FileDialogListView),
85 tr("Empty Basket"), this);
86 emptyBasketAct->setStatusTip(tr("Clear the items marked as 'in the basket'"));
87 connect(emptyBasketAct, SIGNAL(triggered()), this, SLOT(on_action_clear_bought()));
89 QAction *fullListAct = new QAction(QIcon("/usr/share/icons/hicolor/64x64/apps/shopper/full-list.png"),
90 tr("Out Shopping: Full List"), this);
91 fullListAct->setStatusTip(tr("Show the full list (items in the basket will be visible and ticked)"));
92 connect(fullListAct, SIGNAL(triggered()), this, SLOT(on_action_fullList()));
94 QAction *whatsLeftAct = new QAction(QIcon("/usr/share/icons/hicolor/64x64/apps/shopper/whats-left.png"),
95 tr("Out Shopping: What's Left"), this);
96 whatsLeftAct->setStatusTip(tr("Show what's left to get"));
97 connect(whatsLeftAct, SIGNAL(triggered()), this, SLOT(on_action_whatsLeft()));
99 QAction *makingListAct = new QAction(QIcon("/usr/share/icons/hicolor/64x64/apps/shopper/making-list.png"),
100 tr("Making List"), this);
101 makingListAct->setStatusTip(tr("Pick items to get when you go shopping"));
102 connect(makingListAct, SIGNAL(triggered()), this, SLOT(on_action_makingList()));
104 QAction *newAct = new QAction(qApp->style()->standardIcon(QStyle::SP_FileDialogListView),
105 tr("New List"), this);
106 newAct->setStatusTip(tr("Add an item to the list"));
107 connect(newAct, SIGNAL(triggered()), this, SLOT(on_action_file_new()));
109 QAction *openAct = new QAction(qApp->style()->standardIcon(QStyle::SP_DialogOpenButton),
110 tr("Open List"), this);
111 openAct->setStatusTip(tr("Open a new list"));
112 connect(openAct, SIGNAL(triggered()), this, SLOT(on_action_file_open()));
114 QAction *saveAct = new QAction(qApp->style()->standardIcon(QStyle::SP_DialogSaveButton),
115 tr("Save List"), this);
116 saveAct->setStatusTip(tr("Save the list"));
117 connect(saveAct, SIGNAL(triggered()), this, SLOT(on_action_file_save()));
119 QAction *saveAsAct = new QAction(qApp->style()->standardIcon(QStyle::SP_DialogSaveButton),
120 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 *prefsAct = new QAction(qApp->style()->standardIcon(QStyle::SP_DialogSaveButton),
125 tr("Preferences..."), this);
126 prefsAct->setStatusTip(tr("Application preferences"));
127 connect(prefsAct, SIGNAL(triggered()), this, SLOT(on_action_preferences()));
129 QAction *rotateAct = new QAction(QIcon("/usr/share/icons/hicolor/64x64/apps/shopper/rotate.png"),
130 tr("Rotate"), this);
131 prefsAct->setStatusTip(tr("Rotate Application"));
132 connect(rotateAct, SIGNAL(triggered()), this, SLOT(on_action_rotate()));
134 QAction *quitAct = new QAction(qApp->style()->standardIcon(QStyle::SP_BrowserStop),
135 tr("Quit"), this);
136 quitAct->setStatusTip(tr("Finish"));
137 connect(quitAct, SIGNAL(triggered()), this, SLOT(close()));
139 QAction *aboutAct = new QAction(qApp->style()->standardIcon(QStyle::SP_MessageBoxInformation),
140 tr("About..."), this);
141 aboutAct->setStatusTip(tr("About Shopper..."));
142 connect(aboutAct, SIGNAL(triggered()), this, SLOT(on_action_about()));
144 // Defined in class to be available elsewhere
145 nextAct = new QAction(qApp->style()->standardIcon(QStyle::SP_MediaSkipForward),
146 tr("Next Category"), this);
147 nextAct->setStatusTip(tr("View the next category"));
148 connect(nextAct, SIGNAL(triggered()), this, SLOT(on_action_next()));
150 QAction *prevAct = new QAction(qApp->style()->standardIcon(QStyle::SP_MediaSkipBackward),
151 tr("Prev Category"), this);
152 prevAct->setStatusTip(tr("View the previous category"));
153 connect(prevAct, SIGNAL(triggered()), this, SLOT(on_action_prev()));
155 // Menus
156 menuBar()->addAction(addItemAct);
157 menuBar()->addAction(manageCatAct);
159 QMenu *clearMenu = menuBar()->addMenu(tr("Clear List"));
160 clearMenu->addAction(clearWantedAct);
161 clearMenu->addAction(emptyBasketAct);
163 QMenu *modeMenu = menuBar()->addMenu(tr("Mode"));
164 modeMenu->addAction(fullListAct);
165 modeMenu->addAction(whatsLeftAct);
166 modeMenu->addAction(makingListAct);
168 menuBar()->addSeparator();
170 QMenu *fileMenu = menuBar()->addMenu(tr("File"));
171 fileMenu->addAction(newAct);
172 fileMenu->addAction(openAct);
173 fileMenu->addAction(saveAct);
174 fileMenu->addAction(saveAsAct);
176 menuBar()->addAction(prefsAct);
178 QMenu *helpMenu = menuBar()->addMenu(tr("Help"));
179 helpMenu->addAction(aboutAct);
181 menuBar()->addAction(quitAct);
183 // Toolbar
184 buttonBar = addToolBar(tr("Buttons"));
185 buttonBar2 = addToolBar(tr("Navigation"));
186 buttonBar->setIconSize(QSize(40,40));
187 buttonBar2->setIconSize(QSize(40,40));
188 addToolBar(Qt::BottomToolBarArea, buttonBar); // move it to the right area
189 addToolBar(Qt::BottomToolBarArea, buttonBar2); // move it to the right area
190 buttonBar->addAction(addItemAct);
191 buttonBar->addAction(manageCatAct);
192 buttonBar->addSeparator();
193 buttonBar->addAction(fullListAct);
194 buttonBar->addAction(whatsLeftAct);
195 buttonBar->addAction(makingListAct);
196 buttonBar2->addAction(prevAct);
197 buttonBar2->addAction(nextAct);
198 buttonBar2->addSeparator();
199 buttonBar2->addAction(rotateAct);
200 // Status
201 statusBar()->showMessage(tr("Ready"));
203 QIcon shopper_icon("/usr/share/icons/hicolor/64x64/apps/shopper/shopper.png");
204 setWindowIcon(shopper_icon);
206 // Prepare our Settings
207 QCoreApplication::setOrganizationName("dgreaves.com");
208 QCoreApplication::setOrganizationDomain("dgreaves.com");
209 QCoreApplication::setApplicationName("Shopper");
211 // size and position
212 readSettings();
213 timerId = startTimer(0);
215 QLabel *notice = new QLabel("List Loading. Wait just a moment...", this);
216 notice->show();
217 setCentralWidget(notice);
218 _LEAVE;
221 void MainWindow::timerEvent(QTimerEvent *event)
223 killTimer(event->timerId());
224 qApp->processEvents(); // ensure we're quiet
225 qApp->processEvents(); // ensure we're quiet
226 loadList();
229 void MainWindow::loadList()
231 Shopper::List *l;
232 if (mylist == 0) {
233 l = Shopper::List::from_file(filename);
234 if (l) { // If filename valid
235 create_list_view(l);
236 } else { // If filename wasn't read
237 on_action_file_new();
238 while (!mylist) { // mylist and list_view are done in a file_open()
239 DEBUG("Couldn't open " << filename << " for reading\n");
240 on_action_file_open();
241 // FIXME :: Popup dialog box and allow new/open
247 void MainWindow::create_list_view(Shopper::List *list)
249 // Remove the old listview... and any data
250 if (mylist) mylist->deleteLater();
251 if (lv) lv->deleteLater();
252 if (catCombo)
254 if (catCombo->model()) delete catCombo->model();
255 delete catCombo;
257 // and use a list and a new main shopperList widget
258 mylist = list;
259 lv = new Shopper::ListView(*list, this);
260 setCentralWidget(lv);
261 // GUI: set title, add the view, update the menu
262 setWindowTitle("Shopper:"+mylist->modeText());
263 // Make sure there's a menu to show to enable the popup functionality
265 catCombo = new QComboBox();
266 Shopper::CatListModel *clmodel = new Shopper::CatListModel(*mylist);
267 catCombo->setModel(clmodel);
268 catCombo->setModelColumn(1);
269 catCombo->setCurrentIndex(clmodel->currentIndex());
270 connect(catCombo, SIGNAL(activated(int)),
271 this, SLOT(cat_selected(int)));
272 connect(clmodel, SIGNAL(currentChanged(int)),
273 catCombo, SLOT(setCurrentIndex(int)));
274 buttonBar2->insertWidget(nextAct, catCombo);
276 // Update UI when the state changes
277 connect(mylist, SIGNAL(state_changed()),
278 this, SLOT(stateChanged()));
280 _LEAVE;
283 void MainWindow::cat_selected(int i)
285 Q_UNUSED(i)
286 _ENTER;
287 QVariant v = catCombo->itemData(catCombo->currentIndex());
288 Shopper::Category *c = v.value<Shopper::Category*>();
289 mylist->make_category_active(*c);
293 void MainWindow::closeEvent(QCloseEvent *event)
295 _ENTER;
296 QSettings settings;
297 if (settings.value("data/SaveOnExit").toBool())
298 on_action_file_save();
299 writeSettings();
300 if (has_rotation and settings.value("ui/RotateSafe").toBool()) {
301 XRRScreenConfiguration *scr_config;
302 int size;
303 Display* dpy = XOpenDisplay (NULL);
304 int screen = DefaultScreen (dpy);
305 scr_config = XRRGetScreenInfo (dpy, RootWindow (dpy, screen));
306 size = XRRConfigCurrentConfiguration (scr_config, &rotation);
307 XRRSetScreenConfig (dpy, scr_config, RootWindow (dpy, screen), size, RR_Rotate_0, CurrentTime);
308 XRRFreeScreenConfigInfo (scr_config);
309 XCloseDisplay (dpy);
311 event->accept();
314 void MainWindow::on_action_about()
316 QMessageBox::about(this, tr("About Shopper"),
317 tr("<b>Shopper</b> was written by David Greaves (lbt)<br>"
318 "It is Free Software and licensed under the GPL.<br>"
319 "I hope you enjoy using it.<br>"
320 "Please let me know of any problems or suggestions."));
324 void MainWindow::readSettings()
326 QSettings settings;
327 QPoint pos = settings.value("pos", QPoint(200, 200)).toPoint();
328 QSize size = settings.value("size", QSize(400, 400)).toSize();
329 filename = settings.value("filename", filename).toString();
330 resize(size);
331 move(pos);
334 void MainWindow::writeSettings()
336 QSettings settings;
337 settings.setValue("pos", pos());
338 settings.setValue("size", size());
339 settings.setValue("filename", filename);
340 DEBUG("Wrote Settings");
343 // Action handlers
344 // Add item
345 void MainWindow::on_action_add_item()
347 Shopper::ItemAdd itemD(this, mylist);
348 if (itemD.exec() == QDialog::Accepted) {
349 DEBUG("\nADDED:\n");
353 void MainWindow::on_action_manage_category()
355 Shopper::CategoryManage catD(this, mylist);
356 catD.exec();
359 // Category cycle
360 void MainWindow::on_action_next(){ mylist->make_next_category_active(); }
361 void MainWindow::on_action_prev(){ mylist->make_prev_category_active(); }
363 // Clear List
364 void MainWindow::on_action_clear_wanted() { mylist->clear(Shopper::CLEAR_WANTED); }
365 void MainWindow::on_action_clear_bought() { mylist->clear(Shopper::CLEAR_BOUGHT); }
367 // Shopping mode
368 void MainWindow::on_action_fullList() { mylist->set_state(Shopper::OUT_SHOPPING); }
369 void MainWindow::on_action_whatsLeft() { mylist->set_state(Shopper::WHATS_LEFT); }
370 void MainWindow::on_action_makingList() { mylist->set_state(Shopper::MAKING_LIST); }
372 void MainWindow::stateChanged()
374 setWindowTitle("Shopper:" + mylist->modeText());
376 ////////////////////////////////////////////////////////////////
377 // File Handling
378 void MainWindow::on_action_file_new()
380 // FIXME: This really ought to check if the user *WANTS* to save...
381 if (mylist)
382 on_action_file_save();
383 filename="";
384 create_list_view(new Shopper::List(
385 QString(
386 "<list name='Supermarket' state='0'>"
387 "<category name='Aisle 1 - Dairy'>"
388 "<item desc='Cheese' note='note, no smelly cheese' wanted='1' bought='0'/>"
389 "<item desc='Bacon' note='Extra needed for Gary&apos;s butties' wanted='1' bought='0'/>"
390 "</category>"
391 "<category name='Frozen'>"
392 "<item desc='Peas' note='' wanted='1' bought='0'/>"
393 "<item desc='Prawns' note='' wanted='1' bought='0'/>"
394 "</category>"
395 "<category name='Veg'>"
396 "<item desc='Potatoes' note='' wanted='0' bought='0'/>"
397 "<item desc='Brocolli' note='' wanted='1' bought='0'/>"
398 "</category>"
399 "<category name='Shoes'>"
400 "<item desc='Versace' note='For Tracy' wanted='0' bought='0'/>"
401 "</category>"
402 "</list>"
403 )));
405 void MainWindow::on_action_file_open()
407 // Get a filename
408 QFileDialog dialog(this);
409 QString dir = filename.isEmpty() ? QString(HOME_DIR)+ QString(DEFAULT_LIST) : filename ;
410 //Handle the response:
411 if (!(filename = dialog.getOpenFileName(this, "Select a list",dir, tr("Lists (*)"))).isEmpty()) {
412 // Open file and create a view from it.
413 create_list_view(Shopper::List::from_file(filename));
414 writeSettings(); // FIXME this should be on success
415 } else {
416 if (mylist == 0) on_action_file_new(); // If CANCEL pressed and there's no list, make a new one
420 ////////////////////////////////////////////////////////////////
421 void MainWindow::on_action_file_save()
423 DEBUG("Saving....\n");
424 if (filename.isEmpty()) {
425 on_action_file_saveas();
426 } else {
427 mylist->to_file(filename);
430 void MainWindow::on_action_file_saveas()
432 // Get a filename
433 QFileDialog dialog(this);
434 QString dir = HOME_DIR+(filename==""?DEFAULT_LIST:filename);
435 //Handle the response:
436 if (!(filename = dialog.getSaveFileName(this, "Save list as", dir, tr("Lists (*.xml)"))).isEmpty()) {
437 // Open file and create a view from it.
438 writeSettings();
439 mylist->to_file(filename);
442 void MainWindow::on_action_preferences()
444 Shopper::PreferencesDialog prefsD(this);
445 prefsD.exec();
446 lv->update();
449 void MainWindow::keyPressEvent ( QKeyEvent * event )
451 switch (event->key())
453 case Qt::Key_F7: // Zoom in
454 lv->zoomIn();
455 break;
456 case Qt::Key_F8: // Zoom out
457 lv->zoomOut();
458 break;
459 default:
460 QMainWindow::keyPressEvent(event);
461 return;
462 break;
464 return;
467 void MainWindow::on_action_rotate()
469 if (! has_rotation) return;
471 XRRScreenConfiguration *scr_config;
472 // Rotation current_rotation;
473 int size;
474 // int i;
475 Display *dpy;
476 int screen;
478 dpy = XOpenDisplay (0);
479 screen = DefaultScreen (dpy);
480 scr_config = XRRGetScreenInfo (dpy, RootWindow (dpy, screen));
481 size = XRRConfigCurrentConfiguration (scr_config, &rotation);
483 if (rotation == RR_Rotate_0) {
484 QSettings settings;
485 if (settings.value("ui/RotateOtherWay",0).toBool()) {
486 rotation = RR_Rotate_270;
487 } else {
488 rotation = RR_Rotate_90;
490 insertToolBarBreak(buttonBar2);
491 } else {
492 rotation = RR_Rotate_0; // always rotate back to 0
493 removeToolBarBreak(buttonBar2);
496 XRRSetScreenConfig (dpy, scr_config, RootWindow (dpy, screen), size, rotation, CurrentTime);
497 XRRFreeScreenConfigInfo (scr_config);
498 XCloseDisplay (dpy);
502 void MainWindow::on_action_help()