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
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"
31 // class MainWindow : public QMainWindow
32 MainWindow::MainWindow(QString file
) :
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...
48 int xrr_event_base
, xrr_error_base
;
49 int xrr_major
, xrr_minor
;
50 dpy
= XOpenDisplay (0);
53 DEBUG("Couldn't open display\n");
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");
61 DEBUG("Display has rotation\n");
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"),
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
),
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()));
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
);
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
);
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");
213 timerId
= startTimer(0);
215 QLabel
*notice
= new QLabel("List Loading. Wait just a moment...", this);
217 setCentralWidget(notice
);
221 void MainWindow::timerEvent(QTimerEvent
*event
)
223 killTimer(event
->timerId());
224 qApp
->processEvents(); // ensure we're quiet
225 qApp
->processEvents(); // ensure we're quiet
229 void MainWindow::loadList()
233 l
= Shopper::List::from_file(filename
);
234 if (l
) { // If filename valid
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();
254 if (catCombo
->model()) delete catCombo
->model();
257 // and use a list and a new main shopperList widget
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()));
283 void MainWindow::cat_selected(int i
)
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
)
297 if (settings
.value("data/SaveOnExit").toBool())
298 on_action_file_save();
300 if (has_rotation
and settings
.value("ui/RotateSafe").toBool()) {
301 XRRScreenConfiguration
*scr_config
;
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
);
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()
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();
334 void MainWindow::writeSettings()
337 settings
.setValue("pos", pos());
338 settings
.setValue("size", size());
339 settings
.setValue("filename", filename
);
340 DEBUG("Wrote Settings");
345 void MainWindow::on_action_add_item()
347 Shopper::ItemAdd
itemD(this, mylist
);
348 if (itemD
.exec() == QDialog::Accepted
) {
353 void MainWindow::on_action_manage_category()
355 Shopper::CategoryManage
catD(this, mylist
);
360 void MainWindow::on_action_next(){ mylist
->make_next_category_active(); }
361 void MainWindow::on_action_prev(){ mylist
->make_prev_category_active(); }
364 void MainWindow::on_action_clear_wanted() { mylist
->clear(Shopper::CLEAR_WANTED
); }
365 void MainWindow::on_action_clear_bought() { mylist
->clear(Shopper::CLEAR_BOUGHT
); }
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 ////////////////////////////////////////////////////////////////
378 void MainWindow::on_action_file_new()
380 // FIXME: This really ought to check if the user *WANTS* to save...
382 on_action_file_save();
384 create_list_view(new Shopper::List(
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's butties' wanted='1' bought='0'/>"
391 "<category name='Frozen'>"
392 "<item desc='Peas' note='' wanted='1' bought='0'/>"
393 "<item desc='Prawns' note='' wanted='1' bought='0'/>"
395 "<category name='Veg'>"
396 "<item desc='Potatoes' note='' wanted='0' bought='0'/>"
397 "<item desc='Brocolli' note='' wanted='1' bought='0'/>"
399 "<category name='Shoes'>"
400 "<item desc='Versace' note='For Tracy' wanted='0' bought='0'/>"
405 void MainWindow::on_action_file_open()
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
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();
427 mylist
->to_file(filename
);
430 void MainWindow::on_action_file_saveas()
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.
439 mylist
->to_file(filename
);
442 void MainWindow::on_action_preferences()
444 Shopper::PreferencesDialog
prefsD(this);
449 void MainWindow::keyPressEvent ( QKeyEvent
* event
)
451 switch (event
->key())
453 case Qt::Key_F7
: // Zoom in
456 case Qt::Key_F8
: // Zoom out
460 QMainWindow::keyPressEvent(event
);
467 void MainWindow::on_action_rotate()
469 if (! has_rotation
) return;
471 XRRScreenConfiguration
*scr_config
;
472 // Rotation current_rotation;
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
) {
485 if (settings
.value("ui/RotateOtherWay",0).toBool()) {
486 rotation
= RR_Rotate_270
;
488 rotation
= RR_Rotate_90
;
490 insertToolBarBreak(buttonBar2
);
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
);
502 void MainWindow::on_action_help()