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"
33 Shopper::Notify
*notify
;
35 // class MainWindow : public QMainWindow
36 MainWindow::MainWindow(QString file
) :
45 // The constructor handles only 'one off' stuff - main menu,
46 // buttons etc. Anything List specific is separated off to
47 // handle_list() so it can be reused when opening new lists
49 // xrandr detection from adv-backlight...
52 int xrr_event_base
, xrr_error_base
;
53 int xrr_major
, xrr_minor
;
54 dpy
= XOpenDisplay (0);
57 DEBUG("Couldn't open display\n");
60 if (XRRQueryExtension (dpy
, &xrr_event_base
, &xrr_error_base
) == false
61 || XRRQueryVersion (dpy
, &xrr_major
, &xrr_minor
) == 0 || xrr_major
!= 1 || xrr_minor
< 1) {
62 DEBUG("Display has no rotation\n");
65 DEBUG("Display has rotation\n");
73 QAction
*addItemAct
= new QAction(QIcon("/usr/share/icons/hicolor/64x64/apps/shopper/add.png"),
74 tr("Add Item"), this);
75 addItemAct
->setStatusTip(tr("Add an item to the list"));
76 connect(addItemAct
, SIGNAL(triggered()), this, SLOT(on_action_add_item()));
78 QAction
*manageCatAct
= new QAction(QIcon("/usr/share/icons/hicolor/64x64/apps/shopper/categories.png"),
79 tr("Manage Categories"), this);
80 manageCatAct
->setStatusTip(tr("Manage the list of categories"));
81 connect(manageCatAct
, SIGNAL(triggered()), this, SLOT(on_action_manage_category()));
83 QAction
*clearWantedAct
= new QAction(qApp
->style()->standardIcon(QStyle::SP_FileDialogListView
),
84 tr("Clear Wanted"), this);
85 clearWantedAct
->setStatusTip(tr("Clear the items marked as 'wanted'"));
86 connect(clearWantedAct
, SIGNAL(triggered()), this, SLOT(on_action_clear_wanted()));
88 QAction
*emptyBasketAct
= new QAction(qApp
->style()->standardIcon(QStyle::SP_FileDialogListView
),
89 tr("Empty Basket"), this);
90 emptyBasketAct
->setStatusTip(tr("Clear the items marked as 'in the basket'"));
91 connect(emptyBasketAct
, SIGNAL(triggered()), this, SLOT(on_action_clear_bought()));
93 QAction
*fullListAct
= new QAction(QIcon("/usr/share/icons/hicolor/64x64/apps/shopper/full-list.png"),
94 tr("Out Shopping: Full List"), this);
95 fullListAct
->setStatusTip(tr("Show the full list (items in the basket will be visible and ticked)"));
96 connect(fullListAct
, SIGNAL(triggered()), this, SLOT(on_action_fullList()));
98 QAction
*whatsLeftAct
= new QAction(QIcon("/usr/share/icons/hicolor/64x64/apps/shopper/whats-left.png"),
99 tr("Out Shopping: What's Left"), this);
100 whatsLeftAct
->setStatusTip(tr("Show what's left to get"));
101 connect(whatsLeftAct
, SIGNAL(triggered()), this, SLOT(on_action_whatsLeft()));
103 QAction
*makingListAct
= new QAction(QIcon("/usr/share/icons/hicolor/64x64/apps/shopper/making-list.png"),
104 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(qApp
->style()->standardIcon(QStyle::SP_FileDialogListView
),
109 tr("New List"), this);
110 newAct
->setStatusTip(tr("Add an item to the list"));
111 connect(newAct
, SIGNAL(triggered()), this, SLOT(on_action_file_new()));
113 QAction
*openAct
= new QAction(qApp
->style()->standardIcon(QStyle::SP_DialogOpenButton
),
114 tr("Open List"), this);
115 openAct
->setStatusTip(tr("Open a new list"));
116 connect(openAct
, SIGNAL(triggered()), this, SLOT(on_action_file_open()));
118 QAction
*saveAct
= new QAction(qApp
->style()->standardIcon(QStyle::SP_DialogSaveButton
),
119 tr("Save List"), this);
120 saveAct
->setStatusTip(tr("Save the list"));
121 connect(saveAct
, SIGNAL(triggered()), this, SLOT(on_action_file_save()));
123 QAction
*saveAsAct
= new QAction(qApp
->style()->standardIcon(QStyle::SP_DialogSaveButton
),
124 tr("Save List As..."), this);
125 saveAsAct
->setStatusTip(tr("Save the list with a new name"));
126 connect(saveAsAct
, SIGNAL(triggered()), this, SLOT(on_action_file_saveas()));
128 QAction
*prefsAct
= new QAction(qApp
->style()->standardIcon(QStyle::SP_DialogSaveButton
),
129 tr("Preferences..."), this);
130 prefsAct
->setStatusTip(tr("Application preferences"));
131 connect(prefsAct
, SIGNAL(triggered()), this, SLOT(on_action_preferences()));
133 QAction
*rotateAct
= new QAction(QIcon("/usr/share/icons/hicolor/64x64/apps/shopper/rotate.png"),
135 prefsAct
->setStatusTip(tr("Rotate Application"));
136 connect(rotateAct
, SIGNAL(triggered()), this, SLOT(on_action_rotate()));
138 QAction
*quitAct
= new QAction(qApp
->style()->standardIcon(QStyle::SP_BrowserStop
),
140 quitAct
->setStatusTip(tr("Finish"));
141 connect(quitAct
, SIGNAL(triggered()), this, SLOT(on_action_file_quit()));
143 QAction
*aboutAct
= new QAction(qApp
->style()->standardIcon(QStyle::SP_MessageBoxInformation
),
144 tr("About..."), this);
145 aboutAct
->setStatusTip(tr("About Shopper..."));
146 connect(aboutAct
, SIGNAL(triggered()), this, SLOT(on_action_about()));
148 // Defined in class to be available elsewhere
149 nextAct
= new QAction(qApp
->style()->standardIcon(QStyle::SP_MediaSkipForward
),
150 tr("Next Category"), this);
151 nextAct
->setStatusTip(tr("View the next category"));
152 connect(nextAct
, SIGNAL(triggered()), this, SLOT(on_action_next()));
154 QAction
*prevAct
= new QAction(qApp
->style()->standardIcon(QStyle::SP_MediaSkipBackward
),
155 tr("Prev Category"), this);
156 prevAct
->setStatusTip(tr("View the previous category"));
157 connect(prevAct
, SIGNAL(triggered()), this, SLOT(on_action_prev()));
160 menuBar()->addAction(addItemAct
);
161 menuBar()->addAction(manageCatAct
);
163 QMenu
*clearMenu
= menuBar()->addMenu(tr("Clear List"));
164 clearMenu
->addAction(clearWantedAct
);
165 clearMenu
->addAction(emptyBasketAct
);
167 QMenu
*modeMenu
= menuBar()->addMenu(tr("Mode"));
168 modeMenu
->addAction(fullListAct
);
169 modeMenu
->addAction(whatsLeftAct
);
170 modeMenu
->addAction(makingListAct
);
172 menuBar()->addSeparator();
174 QMenu
*fileMenu
= menuBar()->addMenu(tr("File"));
175 fileMenu
->addAction(newAct
);
176 fileMenu
->addAction(openAct
);
177 fileMenu
->addAction(saveAct
);
178 fileMenu
->addAction(saveAsAct
);
180 menuBar()->addAction(prefsAct
);
182 QMenu
*helpMenu
= menuBar()->addMenu(tr("Help"));
183 helpMenu
->addAction(aboutAct
);
185 menuBar()->addAction(quitAct
);
188 buttonBar
= addToolBar(tr("Buttons"));
189 buttonBar2
= addToolBar(tr("Navigation"));
190 buttonBar
->setIconSize(QSize(40,40));
191 buttonBar2
->setIconSize(QSize(40,40));
192 addToolBar(Qt::BottomToolBarArea
, buttonBar
); // move it to the right area
193 addToolBar(Qt::BottomToolBarArea
, buttonBar2
); // move it to the right area
194 buttonBar
->addAction(addItemAct
);
195 buttonBar
->addAction(manageCatAct
);
196 buttonBar
->addSeparator();
197 buttonBar
->addAction(fullListAct
);
198 buttonBar
->addAction(whatsLeftAct
);
199 buttonBar
->addAction(makingListAct
);
200 buttonBar2
->addAction(prevAct
);
201 buttonBar2
->addAction(nextAct
);
202 buttonBar2
->addSeparator();
203 buttonBar2
->addAction(rotateAct
);
205 statusBar()->showMessage(tr("Ready"));
207 // Notify is a SystemTrayIcon used for notify messages
208 // Note this is a globally accessible object - nothing to do with MainWin except we parent it
209 QIcon
shopper_icon("/usr/share/icons/hicolor/64x64/apps/shopper/shopper.png");
210 notify
=new Shopper::Notify(shopper_icon
);
212 setWindowIcon(shopper_icon
);
214 // Prepare our Settings
215 QCoreApplication::setOrganizationName("dgreaves.com");
216 QCoreApplication::setOrganizationDomain("dgreaves.com");
217 QCoreApplication::setApplicationName("Shopper");
221 timerId
= startTimer(0);
223 QLabel
*notice
= new QLabel("List Loading. Wait just a moment...", this);
225 setCentralWidget(notice
);
229 void MainWindow::timerEvent(QTimerEvent
*event
)
231 killTimer(event
->timerId());
232 qApp
->processEvents(); // ensure we're quiet
233 qApp
->processEvents(); // ensure we're quiet
237 void MainWindow::loadList()
241 // notify->showMessage("Shopper", "Loading shopping list");
242 l
= Shopper::List::from_file(filename
);
243 if (l
) { // If filename valid
245 } else { // If filename wasn't read
246 on_action_file_new();
247 while (!mylist
) { // mylist and list_view are done in a file_open()
248 DEBUG("Couldn't open " << filename
<< " for reading\n");
249 on_action_file_open();
250 // FIXME :: Popup dialog box and allow new/open
256 void MainWindow::create_list_view(Shopper::List
*list
)
258 // Remove the old listview... and any data
259 if (mylist
) mylist
->deleteLater();
260 if (lv
) lv
->deleteLater();
263 if (catCombo
->model()) delete catCombo
->model();
266 // and use a list and a new main shopperList widget
268 lv
= new Shopper::ListView(*list
, this);
269 setCentralWidget(lv
);
270 // GUI: set title, add the view, update the menu
271 setWindowTitle("Shopper:"+mylist
->modeText());
272 // Make sure there's a menu to show to enable the popup functionality
274 catCombo
= new QComboBox();
275 Shopper::CatListModel
*clmodel
= new Shopper::CatListModel(*mylist
);
276 catCombo
->setModel(clmodel
);
277 catCombo
->setModelColumn(1);
278 catCombo
->setCurrentIndex(clmodel
->currentIndex());
279 connect(catCombo
, SIGNAL(activated(int)),
280 this, SLOT(cat_selected(int)));
281 connect(clmodel
, SIGNAL(currentChanged(int)),
282 catCombo
, SLOT(setCurrentIndex(int)));
283 buttonBar2
->insertWidget(nextAct
, catCombo
);
285 // Update UI when the state changes
286 connect(mylist
, SIGNAL(state_changed()),
287 this, SLOT(stateChanged()));
292 void MainWindow::cat_selected(int i
)
296 QVariant v
= catCombo
->itemData(catCombo
->currentIndex());
297 Shopper::Category
*c
= v
.value
<Shopper::Category
*>();
298 mylist
->make_category_active(*c
);
302 void MainWindow::closeEvent(QCloseEvent
*event
)
306 on_action_file_quit();
309 void MainWindow::on_action_about()
311 QMessageBox::about(this, tr("About Shopper"),
312 tr("<b>Shopper</b> was written by David Greaves (lbt)<br>"
313 "It is Free Software and licensed under the GPL.<br>"
314 "I hope you enjoy using it.<br>"
315 "Please let me know of any problems or suggestions."));
319 void MainWindow::readSettings()
322 QPoint pos
= settings
.value("pos", QPoint(200, 200)).toPoint();
323 QSize size
= settings
.value("size", QSize(400, 400)).toSize();
324 filename
= settings
.value("filename", filename
).toString();
329 void MainWindow::writeSettings()
332 settings
.setValue("pos", pos());
333 settings
.setValue("size", size());
334 settings
.setValue("filename", filename
);
335 DEBUG("Wrote Settings");
340 void MainWindow::on_action_add_item()
342 Shopper::ItemAdd
itemD(this, mylist
);
343 if (itemD
.exec() == QDialog::Accepted
) {
348 void MainWindow::on_action_manage_category()
350 Shopper::CategoryManage
catD(this, mylist
);
355 void MainWindow::on_action_next(){ mylist
->make_next_category_active(); }
356 void MainWindow::on_action_prev(){ mylist
->make_prev_category_active(); }
359 void MainWindow::on_action_clear_wanted() { mylist
->clear(Shopper::CLEAR_WANTED
); }
360 void MainWindow::on_action_clear_bought() { mylist
->clear(Shopper::CLEAR_BOUGHT
); }
363 void MainWindow::on_action_fullList() { mylist
->set_state(Shopper::OUT_SHOPPING
); }
364 void MainWindow::on_action_whatsLeft() { mylist
->set_state(Shopper::WHATS_LEFT
); }
365 void MainWindow::on_action_makingList() { mylist
->set_state(Shopper::MAKING_LIST
); }
367 void MainWindow::stateChanged()
369 setWindowTitle("Shopper:" + mylist
->modeText());
371 ////////////////////////////////////////////////////////////////
373 void MainWindow::on_action_file_new()
375 // FIXME: This really ought to check if the user *WANTS* to save...
377 on_action_file_save();
379 create_list_view(new Shopper::List(
381 "<list name='Supermarket' state='0'>"
382 "<category name='Aisle 1 - Dairy'>"
383 "<item desc='Cheese' note='note, no smelly cheese' wanted='1' bought='0'/>"
384 "<item desc='Bacon' note='Extra needed for Gary's butties' wanted='1' bought='0'/>"
386 "<category name='Frozen'>"
387 "<item desc='Peas' note='' wanted='1' bought='0'/>"
388 "<item desc='Prawns' note='' wanted='1' bought='0'/>"
390 "<category name='Veg'>"
391 "<item desc='Potatoes' note='' wanted='0' bought='0'/>"
392 "<item desc='Brocolli' note='' wanted='1' bought='0'/>"
394 "<category name='Shoes'>"
395 "<item desc='Versace' note='For Tracy' wanted='0' bought='0'/>"
399 notify
->showMessage("Shopper", "Created a sample shopping list");
401 void MainWindow::on_action_file_open()
404 QFileDialog
dialog(this);
405 QString dir
= HOME_DIR
+(filename
==""?DEFAULT_LIST
:filename
);
406 //Handle the response:
407 if (!(filename
= dialog
.getOpenFileName(this, "Select a list",dir
, tr("Lists (*)"))).isEmpty()) {
408 // Open file and create a view from it.
409 create_list_view(Shopper::List::from_file(filename
));
410 writeSettings(); // FIXME this should be on success
412 if (mylist
== 0) on_action_file_new(); // If CANCEL pressed and there's no list, make a new one
416 ////////////////////////////////////////////////////////////////
417 void MainWindow::on_action_file_save()
419 DEBUG("Saving....\n");
420 if (filename
.isEmpty()) {
421 on_action_file_saveas();
423 mylist
->to_file(filename
);
426 void MainWindow::on_action_file_saveas()
429 QFileDialog
dialog(this);
430 QString dir
= HOME_DIR
+(filename
==""?DEFAULT_LIST
:filename
);
431 //Handle the response:
432 if (!(filename
= dialog
.getSaveFileName(this, "Save list as", dir
, tr("Lists (*.xml)"))).isEmpty()) {
433 // Open file and create a view from it.
435 mylist
->to_file(filename
);
438 void MainWindow::on_action_preferences()
440 Shopper::PreferencesDialog
prefsD(this);
444 void MainWindow::on_action_file_quit()
448 if (settings
.value("data/SaveOnExit").toBool())
449 on_action_file_save();
451 if (has_rotation
and settings
.value("ui/RotateSafe").toBool()) {
452 XRRScreenConfiguration
*scr_config
;
454 Display
* dpy
= XOpenDisplay (NULL
);
455 int screen
= DefaultScreen (dpy
);
456 scr_config
= XRRGetScreenInfo (dpy
, RootWindow (dpy
, screen
));
457 size
= XRRConfigCurrentConfiguration (scr_config
, &rotation
);
458 XRRSetScreenConfig (dpy
, scr_config
, RootWindow (dpy
, screen
), size
, RR_Rotate_0
, CurrentTime
);
459 XRRFreeScreenConfigInfo (scr_config
);
462 close(); //Closes the main window to quit
465 bool MainWindow::on_signal_window_closed()
468 on_action_file_quit();
469 return true; // propagate signal so hide() happens
472 void MainWindow::keyPressEvent ( QKeyEvent
* event
)
474 switch (event
->key())
476 case Qt::Key_F7
: // Zoom in
479 case Qt::Key_F8
: // Zoom out
483 QMainWindow::keyPressEvent(event
);
490 void MainWindow::on_action_rotate()
492 if (! has_rotation
) return;
494 XRRScreenConfiguration
*scr_config
;
495 // Rotation current_rotation;
501 dpy
= XOpenDisplay (0);
502 screen
= DefaultScreen (dpy
);
503 scr_config
= XRRGetScreenInfo (dpy
, RootWindow (dpy
, screen
));
504 size
= XRRConfigCurrentConfiguration (scr_config
, &rotation
);
506 if (rotation
== RR_Rotate_0
) {
508 if (settings
.value("ui/RotateOtherWay",0).toBool()) {
509 rotation
= RR_Rotate_270
;
511 rotation
= RR_Rotate_90
;
513 insertToolBarBreak(buttonBar2
);
515 rotation
= RR_Rotate_0
; // always rotate back to 0
516 removeToolBarBreak(buttonBar2
);
519 XRRSetScreenConfig (dpy
, scr_config
, RootWindow (dpy
, screen
), size
, rotation
, CurrentTime
);
520 XRRFreeScreenConfigInfo (scr_config
);
525 void MainWindow::on_action_help()