diff --git a/CHANGES b/CHANGES
index fef667d..15b697f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,9 @@
+2002-10-28 #################### PortaBase 1.1 ####################
+
+2002-10-28 JMB Multiple column sorting
+
+ Added support for sorting on multiple columns.
+
2002-10-26 JMB Note column type, minor bugfixes
Added support for "Note" columns (a.k.a. multi-line text fields).
diff --git a/README b/README
index a8584b2..87809f8 100644
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-PortaBase 1.0 (October 2002)
+PortaBase 1.1 (October 2002)
----------------------------
PortaBase (portable database) is a personal database application written for
the Linux-based models of Sharp Zaurus PDA (and should work on any other Linux
@@ -6,10 +6,11 @@ PDA using the Qtopia environment.)
The main features PortaBase currently has are:
- One data table per file
-- String, Integer, Decimal, and Boolean column types
+- String, Integer, Decimal, Boolean, and Note (multi-line text) column types
- Add, edit, and delete rows of data
- Custom data views (subsets of the columns in any order)
-- Sort the rows by any column in ascending or descending order
+- Sort the rows by any combination of columns, each in ascending or descending
+ order
- Page navigation buttons, with a custom number of rows per page
- Add, delete, rearrange, and rename columns at any time
- Specify default values for columns
@@ -17,9 +18,21 @@ The main features PortaBase currently has are:
- Import data from CSV files
- Export data to CSV files
-See the help file (help/portabase.html) for more information on features and
-usage. This help file is also the online help for the application, available
-by clicking the "?" button at the top right of any screen in PortaBase.
+See the help file (help/html/portabase.html) for more information on features
+and usage. This help file is also the online help for the application,
+available by clicking the "?" button at the top right of any screen in
+PortaBase.
+
+Installation
+------------
+Note that there are *two* packages that must be installed in order to run
+PortaBase; since many people didn't realize this for version 1.0, they are
+now only distributed together in a zip file along with this README file. First
+install the libmetakit1 .ipk file onto the Zaurus, then install the
+portabase .ipk file. (The MetaKit library is packaged separately because
+it is likely to be used by other applications in the near future.) Once
+both packages have been installed, PortaBase can be launched from the
+Applications tab and the two .ipk files may be safely deleted.
Technical Info
--------------
diff --git a/TODO b/TODO
index b6a63a5..2cd759a 100644
--- a/TODO
+++ b/TODO
@@ -1,4 +1,3 @@
-Multiple column sorting
Filtering/Searching
Date column type
Date and time column type
diff --git a/database.cpp b/database.cpp
index acb01c7..a03521f 100644
--- a/database.cpp
+++ b/database.cpp
@@ -19,27 +19,35 @@
#include "portabase.h"
#include "view.h"
-Database::Database(QString path, bool *ok) : curView(0), Id("_id"), cIndex("_cindex"), cName("_cname"), cType("_ctype"), cDefault("_cdefault"), vName("_vname"), vRpp("_vrpp"), vcView("_vcview"), vcIndex("_vcindex"), vcName("_vcname"), vcWidth("_vcwidth"), vcSortIndex("_vcsortindex"), vcAscending("_vcascending"), vcFilter("_vcfilter"), gVersion("_gversion"), gView("_gview")
+Database::Database(QString path, bool *ok) : curView(0), Id("_id"), cIndex("_cindex"), cName("_cname"), cType("_ctype"), cDefault("_cdefault"), vName("_vname"), vRpp("_vrpp"), vcView("_vcview"), vcIndex("_vcindex"), vcName("_vcname"), vcWidth("_vcwidth"), sName("_sname"), scSort("_scsort"), scIndex("_scindex"), scName("_scname"), scDesc("_scdesc"), gVersion("_gversion"), gView("_gview"), gSort("_gsort")
{
checkedPixmap = Resource::loadPixmap("portabase/checked");
uncheckedPixmap = Resource::loadPixmap("portabase/unchecked");
file = new c4_Storage(path, TRUE);
- global = file->GetAs("_global[_gversion:I,_gview:S]");
+ global = file->GetAs("_global[_gversion:I,_gview:S,_gsort:S]");
if (global.GetSize() == 0) {
- global.Add(gVersion [FILE_VERSION] + gView ["_all"]);
+ global.Add(gVersion [FILE_VERSION] + gView ["_all"] + gSort [""]);
*ok = TRUE;
}
else if (gVersion (global[0]) > FILE_VERSION) {
*ok = FALSE;
}
+ else if (gVersion (global[0]) == 1) {
+ // sorting name added in file version 2
+ gSort (global[0]) = "";
+ gVersion (global[0]) = FILE_VERSION;
+ *ok = TRUE;
+ }
else {
*ok = TRUE;
}
if (*ok) {
columns = file->GetAs("_columns[_cindex:I,_cname:S,_ctype:I,_cdefault:S]");
views = file->GetAs("_views[_vname:S,_vrpp:I]");
- viewColumns = file->GetAs("_viewcolumns[_vcview:S,_vcindex:I,_vcname:S,_vcwidth:I,_vcsortindex:I,_vcascending:I,_vcfilter:S]");
+ viewColumns = file->GetAs("_viewcolumns[_vcview:S,_vcindex:I,_vcname:S,_vcwidth:I]");
+ sorts = file->GetAs("_sorts[_sname:S]");
+ sortColumns = file->GetAs("_sortcolumns[_scsort:S,_scindex:I,_scname:S,_scdesc:I]");
data = file->GetAs(formatString());
maxId = data.GetSize() - 1;
*ok = TRUE;
@@ -110,9 +118,6 @@ void Database::addView(QString name, QStringList names)
vcIndex (colRow) = i;
vcName (colRow) = names[i];
vcWidth (colRow) = 60;
- vcSortIndex (colRow) = 0;
- vcAscending (colRow) = 0;
- vcFilter (colRow) = "";
viewColumns.Add(colRow);
}
}
@@ -283,6 +288,12 @@ void Database::deleteColumn(QString name)
QString viewName(vName (views[i]));
deleteViewColumn(viewName, name);
}
+ // remove the column from any sortings containing it
+ count = sorts.GetSize();
+ for (int i = 0; i < count; i++) {
+ QString sortName(sName (sorts[i]));
+ deleteSortingColumn(sortName, name);
+ }
// remove the column from the definition
columns.RemoveAt(index);
}
@@ -295,6 +306,12 @@ void Database::renameColumn(QString oldName, QString newName)
vcName (viewColumns[nextIndex]) = newName;
nextIndex = viewColumns.Find(vcName [oldName]);
}
+ // rename the column in any sortings containing it
+ nextIndex = sortColumns.Find(scName [oldName]);
+ while (nextIndex != -1) {
+ scName (sortColumns[nextIndex]) = newName;
+ nextIndex = sortColumns.Find(scName [oldName]);
+ }
// rename the column in the format definition
int index = columns.Find(cName [oldName]);
cName (columns[index]) = newName;
@@ -358,36 +375,152 @@ QStringList Database::getRow(int rowId)
return results;
}
-c4_View Database::sortData(QString column, bool ascending)
+QString Database::currentSorting()
{
- int type = getType(column);
- if (type == INTEGER || type == BOOLEAN) {
- c4_IntProp prop(column);
- if (ascending) {
- return data.SortOn(prop);
- }
- else {
- return data.SortOnReverse(prop, prop);
+ QString sortName(gSort (global[0]));
+ return sortName;
+}
+
+QStringList Database::listSortings()
+{
+ c4_View sorted = sorts.SortOn(sName);
+ int size = sorted.GetSize();
+ QStringList list;
+ for (int i = 0; i < size; i++) {
+ QString name(sName (sorted[i]));
+ list.append(name);
+ }
+ return list;
+}
+
+bool Database::getSortingInfo(QString sortingName, QStringList *allCols,
+ QStringList *descCols)
+{
+ c4_View temp = sortColumns.Select(scSort [sortingName]);
+ int count = temp.GetSize();
+ if (count == 0) {
+ // non-existent or empty sorting; nothing to do
+ return FALSE;
+ }
+ temp = temp.SortOn(scIndex);
+ for (int i = 0; i < count; i++) {
+ QString name(scName (temp[i]));
+ allCols->append(name);
+ if (scDesc (temp[i]) == 1) {
+ descCols->append(name);
}
}
- else if (type == FLOAT) {
- c4_FloatProp prop(column);
- if (ascending) {
- return data.SortOn(prop);
+ return TRUE;
+}
+
+void Database::addSorting(QString name, QStringList allCols,
+ QStringList descCols)
+{
+ sorts.Add(sName [name]);
+ int count = allCols.count();
+ for (int i = 0; i < count; i++) {
+ c4_Row colRow;
+ scSort (colRow) = name;
+ scIndex (colRow) = i;
+ scName (colRow) = allCols[i];
+ if (descCols.findIndex(allCols[i]) == -1) {
+ scDesc (colRow) = 0;
}
else {
- return data.SortOnReverse(prop, prop);
+ scDesc (colRow) = 1;
}
+ sortColumns.Add(colRow);
+ }
+}
+
+void Database::deleteSorting(QString name)
+{
+ int index = sorts.Find(sName [name]);
+ if (index == -1) {
+ return;
+ }
+ sorts.RemoveAt(index);
+ // delete the sorting's columns
+ int nextIndex = sortColumns.Find(scSort [name]);
+ while (nextIndex != -1) {
+ sortColumns.RemoveAt(nextIndex);
+ nextIndex = sortColumns.Find(scSort [name]);
+ }
+}
+
+void Database::deleteSortingColumn(QString sortName, QString columnName)
+{
+ int removeIndex = sortColumns.Find(scSort [sortName]
+ + scName [columnName]);
+ if (removeIndex == -1) {
+ // no such column in this sorting
+ return;
+ }
+ int position = scIndex (sortColumns[removeIndex]);
+ position++;
+ int nextIndex = sortColumns.Find(scSort [sortName] + scIndex [position]);
+ while (nextIndex != -1) {
+ scIndex (sortColumns[nextIndex]) = position - 1;
+ position++;
+ nextIndex = sortColumns.Find(scSort [sortName] + scIndex [position]);
+ }
+ sortColumns.RemoveAt(removeIndex);
+}
+
+c4_View Database::sortData(QString column, bool ascending)
+{
+ QStringList colNames;
+ colNames.append(column);
+ QStringList descNames;
+ if (!ascending) {
+ descNames.append(column);
+ }
+ deleteSorting("_single");
+ addSorting("_single", colNames, descNames);
+ gSort (global[0]) = "_single";
+ c4_View sortView = createEmptyView(colNames);
+ if (ascending) {
+ return data.SortOn(sortView);
}
else {
- c4_StringProp prop(column);
- if (ascending) {
- return data.SortOn(prop);
+ return data.SortOnReverse(sortView, sortView);
+ }
+}
+
+c4_View Database::sortData(QString sortingName)
+{
+ gSort (global[0]) = sortingName;
+ QStringList allCols;
+ QStringList descCols;
+ if (!getSortingInfo(sortingName, &allCols, &descCols)) {
+ return data;
+ }
+ c4_View allView = createEmptyView(allCols);
+ c4_View descView = createEmptyView(descCols);
+ return data.SortOnReverse(allView, descView);
+}
+
+c4_View Database::createEmptyView(QStringList colNames)
+{
+ int count = colNames.count();
+ c4_View result;
+ for (int i = 0; i < count; i++) {
+ QString name = colNames[i];
+ int type = getType(name);
+ if (type == INTEGER || type == BOOLEAN) {
+ c4_IntProp prop(name);
+ result.AddProperty(prop);
+ }
+ else if (type == FLOAT) {
+ c4_FloatProp prop(name);
+ result.AddProperty(prop);
}
else {
- return data.SortOnReverse(prop, prop);
+ c4_StringProp prop(name);
+ result.AddProperty(prop);
}
}
+ return result;
}
QString Database::addRow(QStringList values)
@@ -487,9 +620,6 @@ void Database::addViewColumn(QString viewName, QString columnName)
vcIndex (colRow) = cols.GetSize();
vcName (colRow) = columnName;
vcWidth (colRow) = 60;
- vcSortIndex (colRow) = 0;
- vcAscending (colRow) = 0;
- vcFilter (colRow) = "";
viewColumns.Add(colRow);
}
diff --git a/database.h b/database.h
index bddc4ae..4586acf 100644
--- a/database.h
+++ b/database.h
@@ -27,6 +27,7 @@ class Database
public:
Database(QString path, bool *ok);
~Database();
+
QString currentView();
View *getView(QString name);
QStringList listViews();
@@ -34,6 +35,7 @@ class Database
void deleteView(QString name);
void setViewColWidths(int *widths);
void setViewRowsPerPage(int rpp);
+
QStringList listColumns();
int getIndex(QString column);
void setIndex(QString column, int index);
@@ -51,7 +53,17 @@ class Database
void setViewColumnSequence(QString viewName, QStringList colNames);
void updateDataFormat();
QStringList getRow(int rowId);
+
+ QString currentSorting();
+ QStringList listSortings();
+ bool getSortingInfo(QString sortingName, QStringList *allCols,
+ QStringList *descCols);
+ void addSorting(QString name, QStringList allCols, QStringList descCols);
+ void deleteSorting(QString name);
+ void deleteSortingColumn(QString sortName, QString columnName);
c4_View sortData(QString column, bool ascending);
+ c4_View sortData(QString sortingName);
+
QString addRow(QStringList values);
void updateRow(int rowId, QStringList values);
void deleteRow(int id);
@@ -62,6 +74,7 @@ class Database
private:
int *listTypes();
+ c4_View createEmptyView(QStringList colNames);
QString formatString();
private:
@@ -71,6 +84,8 @@ class Database
c4_View columns;
c4_View views;
c4_View viewColumns;
+ c4_View sorts;
+ c4_View sortColumns;
c4_View global;
c4_View data;
View *curView;
@@ -89,12 +104,17 @@ class Database
c4_IntProp vcIndex;
c4_StringProp vcName;
c4_IntProp vcWidth;
- c4_IntProp vcSortIndex;
- c4_IntProp vcAscending;
- c4_StringProp vcFilter;
+ // "_sorts" view
+ c4_StringProp sName;
+ // "_sortcolumns" view
+ c4_StringProp scSort;
+ c4_IntProp scIndex;
+ c4_StringProp scName;
+ c4_IntProp scDesc;
// "_global" view
c4_IntProp gVersion;
c4_StringProp gView;
+ c4_StringProp gSort;
};
#endif
diff --git a/help/html/portabase.html b/help/html/portabase.html
index de5539c..b4c99e9 100644
--- a/help/html/portabase.html
+++ b/help/html/portabase.html
@@ -1,11 +1,11 @@
- PortaBase 1.0
+ PortaBase 1.1
-PortaBase 1.0
- A portable database -
+PortaBase 1.1
- A portable database -
1)Introduction
2)File selector
@@ -14,8 +14,9 @@
5)Data viewer
6)Row editor
7)Note editor/viewer
-8)Data import
-9)Data export
+8)Sorting editor
+9)Data import
+10)Data export
1)Introduction
@@ -23,9 +24,11 @@
custom tables of data. Typical uses are media inventories, reference
charts, etc. Notable features include:
- - String, Integer, Decimal, Boolean, and Note (multi-line text) column types
+ - String, Integer, Decimal, Boolean, and Note (multi-line text) column
+ types
- Custom data views (subsets of the columns in any order)
- - Sort the rows by any column in ascending or descending order
+ - Sort the rows by any combination of columns, each in ascending or
+ descending order
- Page navigation buttons, with custom number of rows per page
- Add, delete, rearrange, and rename columns at any time
- Specify default values for columns
@@ -108,6 +111,7 @@
- File menu
- Row menu
- View menu
+- Sort menu
- Toolbar buttons
Navigation
@@ -175,6 +179,27 @@
switch to a different view, simply select the one you want to see from
the menu.
+Sort menu
+The "Sort" menu allows you to select and manage sorting configurations.
+The top section of this menu has three options:
+
+- Add - Add a new sorting to the database file. Choosing this
+option launches the sorting editor with no name
+specified and none of the columns checked.
+- Edit - Edit the currently used sorting (only available if a
+named sorting is in use). Choosing this option launches the sorting
+editor with the current sorting's name and settings.
+- Delete - Delete the currently used sorting (only available if
+a named sorting is in use).
+
+Below these items in the menu is a list of all the sortings defined for
+the current database, with a check next to the one currently in use
+(if any). To change the way the rows are sorted, simply select the
+sorting configuration you want to use from the menu. If you just want
+to sort by a single column that is displayed in the current view, it is
+probably faster to use the "click on a column label to sort" feature
+instead.
+
Toolbar buttons
There is a toolbar of four buttons that provide quick access to commonly
used operations. These are:
@@ -218,7 +243,29 @@
of the Note editor is shown. Click either "OK" or "X" to dismiss it and
return to the data viewer.
-8)Data import
+8)Sorting editor
+Clicking on a column label in the data viewer is a convenient way to
+sort the database contents by a single column. But sometimes you may want
+to sort by a combination of columns; sort by the content of column A, within
+groups of the same value of A sort by the content of column B, etc. To do
+this or to sort by a column that isn't shown in the current view, you need
+to define a named sorting.
+
+Sortings are defined using the sorting editor dialog, which is very
+similar to the view editor dialog. At the top of
+the dialog is a text box containing the name of the sorting; changing this
+renames the sorting. Below that is a table showing the names of all the
+columns in the database, with a checkbox next to each indicating whether
+or not it is to be sorted. Click this checkbox to add or remove the column
+from the set of ones to be sorted. The third column in the table shows either
+"Ascending" or "Descending" for checked rows, indicating which direction to
+sort in; click this indicator to change it to the opposite value. At the
+bottom of the dialog are "Up" and "Down" buttons for moving the selected row
+up or down in the table, changing the sequence in which the columns are
+sorted. Click "OK" to accept the currently shown settings or "X" to revert
+to the previous values (or to cancel adding a new sorting).
+
+9)Data import
PortaBase can import rows of data from CSV (Comma-Separated Value) files.
This allows you to import data exported from a spreadsheet or another
database program. To do this:
@@ -235,7 +282,7 @@
other files can be added by repeating this process, or another copy of the
same rows can be added by importing the same file again.
-9)Data export
+10)Data export
PortaBase can also export database rows to CSV files. The inverse of data
import, this allows you to move database content into a spreadsheet or
other database program for further manipulation (among other uses). To
diff --git a/package/control b/package/control
index ef237b1..8dbbb06 100644
--- a/package/control
+++ b/package/control
@@ -1,7 +1,7 @@
Package: portabase
Priority: optional
Section: Applications
-Version: 1.0
+Version: 1.1
Architecture: arm
Maintainer: Jeremy Bowman
Depends: qpe-base, libmetakit1
diff --git a/portabase.cpp b/portabase.cpp
index 314d8c9..8c97ad4 100644
--- a/portabase.cpp
+++ b/portabase.cpp
@@ -26,6 +26,7 @@
#include "importdialog.h"
#include "inputdialog.h"
#include "portabase.h"
+#include "sorteditor.h"
#include "viewdisplay.h"
#include "vieweditor.h"
@@ -44,11 +45,15 @@ PortaBase::PortaBase(QWidget *parent, const char *name, WFlags f)
QPopupMenu *row = new QPopupMenu(this);
view = new QPopupMenu(this);
view->setCheckable(TRUE);
+ sort = new QPopupMenu(this);
+ sort->setCheckable(TRUE);
toolbar = new QPEToolBar(this);
- fileSaveAction = new QAction(tr("Save"),
- Resource::loadPixmap("portabase/save"),
+ QIconSet saveIcons = Resource::loadIconSet("portabase/save");
+ //QPixmap disabledSave = Resource::loadPixmap("portabase/save_disabled");
+ //saveIcons.setPixmap(disabledSave, QIconSet::Small, QIconSet::Disabled);
+ fileSaveAction = new QAction(tr("Save"), saveIcons,
QString::null, 0, this, 0);
connect(fileSaveAction, SIGNAL(activated()), this, SLOT(save()));
fileSaveAction->addTo(toolbar);
@@ -96,9 +101,18 @@ PortaBase::PortaBase(QWidget *parent, const char *name, WFlags f)
SLOT(viewAllColumns()));
connect(view, SIGNAL(activated(int)), this, SLOT(changeView(int)));
+ sort->insertItem(addIcons, tr("Add"), this, SLOT(addSorting()));
+ editSortId = sort->insertItem(editIcons, tr("Edit"),
+ this, SLOT(editSorting()));
+ deleteSortId = sort->insertItem(deleteIcons, tr("Delete"),
+ this, SLOT(deleteSorting()));
+ sort->insertSeparator();
+ connect(sort, SIGNAL(activated(int)), this, SLOT(changeSorting(int)));
+
mb->insertItem(tr("File"), file);
mb->insertItem(tr("Row"), row);
mb->insertItem(tr("View"), view);
+ mb->insertItem(tr("Sort"), sort);
mainStack = new QWidgetStack(this);
setCentralWidget(mainStack);
@@ -133,6 +147,7 @@ bool PortaBase::editColumns()
db->addView("_all", db->listColumns());
viewer->setDatabase(db);
rebuildViewMenu();
+ rebuildSortMenu();
}
else {
viewAllColumns();
@@ -213,6 +228,7 @@ void PortaBase::openFile(const DocLnk &f)
toolbar->show();
updateCaption();
rebuildViewMenu();
+ rebuildSortMenu();
}
void PortaBase::fileOpen()
@@ -344,6 +360,16 @@ void PortaBase::changeView(int id)
}
}
+void PortaBase::changeSorting(int id)
+{
+ int index = sortIds.findIndex(id);
+ if (index != -1) {
+ viewer->setSorting(sortNames[index]);
+ updateSortMenu();
+ setEdited(TRUE);
+ }
+}
+
void PortaBase::rebuildViewMenu()
{
// remove old view names
@@ -363,6 +389,25 @@ void PortaBase::rebuildViewMenu()
updateViewMenu();
}
+void PortaBase::rebuildSortMenu()
+{
+ // remove old sorting names
+ int count = sortNames.count();
+ for (int i = 0; i < count; i++) {
+ sort->removeItem(sortIds[i]);
+ }
+ sortIds.clear();
+ // add new sorting names
+ sortNames = db->listSortings();
+ sortNames.remove("_single");
+ count = sortNames.count();
+ for (int i = 0; i < count; i++) {
+ int id = sort->insertItem(sortNames[i]);
+ sortIds.append(id);
+ }
+ updateSortMenu();
+}
+
void PortaBase::updateViewMenu()
{
QString viewName = db->currentView();
@@ -387,6 +432,28 @@ void PortaBase::updateViewMenu()
}
}
+void PortaBase::updateSortMenu()
+{
+ QString sortName = db->currentSorting();
+ if (sortName == "" || sortName == "_single") {
+ sort->setItemEnabled(editSortId, FALSE);
+ sort->setItemEnabled(deleteSortId, FALSE);
+ }
+ else {
+ sort->setItemEnabled(editSortId, TRUE);
+ sort->setItemEnabled(deleteSortId, TRUE);
+ }
+ int count = sortNames.count();
+ for (int i = 0; i < count; i++) {
+ if (sortName == sortNames[i]) {
+ sort->setItemChecked(sortIds[i], TRUE);
+ }
+ else {
+ sort->setItemChecked(sortIds[i], FALSE);
+ }
+ }
+}
+
void PortaBase::editView()
{
ViewEditor editor;
@@ -404,6 +471,22 @@ void PortaBase::editView()
}
}
+void PortaBase::editSorting()
+{
+ SortEditor editor;
+ QString sortingName = db->currentSorting();
+ if (editor.edit(db, sortingName)) {
+ editor.applyChanges();
+ QString newName = editor.getName();
+ viewer->setSorting(newName);
+ // sort menu is unchanged unless the sorting's name changed
+ if (sortingName != newName) {
+ rebuildSortMenu();
+ }
+ setEdited(TRUE);
+ }
+}
+
void PortaBase::addView()
{
ViewEditor editor;
@@ -416,13 +499,31 @@ void PortaBase::addView()
}
}
+void PortaBase::addSorting()
+{
+ SortEditor editor;
+ if (editor.edit(db, "")) {
+ editor.applyChanges();
+ viewer->setSorting(editor.getName());
+ rebuildSortMenu();
+ setEdited(TRUE);
+ }
+}
+
void PortaBase::deleteView()
{
viewer->closeView();
db->deleteView(db->currentView());
viewer->setView("_all");
rebuildViewMenu();
- updateViewMenu();
+ setEdited(TRUE);
+}
+
+void PortaBase::deleteSorting()
+{
+ db->deleteSorting(db->currentSorting());
+ viewer->setSorting("");
+ rebuildSortMenu();
setEdited(TRUE);
}
diff --git a/portabase.h b/portabase.h
index d6bd23f..cfbe8ee 100644
--- a/portabase.h
+++ b/portabase.h
@@ -37,6 +37,7 @@ class PortaBase: public QMainWindow
void setEdited(bool y);
void setRowSelected(bool y);
static QPixmap getNotePixmap();
+ void updateSortMenu();
public slots:
void setDocument(const QString&);
@@ -56,6 +57,10 @@ private slots:
void addView();
void editView();
void deleteView();
+ void changeSorting(int index);
+ void addSorting();
+ void editSorting();
+ void deleteSorting();
protected:
void closeEvent(QCloseEvent *e);
@@ -65,6 +70,7 @@ private slots:
void updateCaption(const QString &name=QString::null);
void rebuildViewMenu();
void updateViewMenu();
+ void rebuildSortMenu();
void closeViewer();
private:
@@ -77,6 +83,7 @@ private slots:
QAction *rowEditAction;
QAction *rowDeleteAction;
QPopupMenu *view;
+ QPopupMenu *sort;
DocLnk *doc;
ViewDisplay *viewer;
bool newdb;
@@ -85,6 +92,10 @@ private slots:
int deleteViewId;
QStringList viewNames;
IntList viewIds;
+ int editSortId;
+ int deleteSortId;
+ QStringList sortNames;
+ IntList sortIds;
bool isEdited;
};
diff --git a/portabase.pro b/portabase.pro
index b41ba62..b441653 100644
--- a/portabase.pro
+++ b/portabase.pro
@@ -1,8 +1,8 @@
TEMPLATE = app
#CONFIG = qt warn_on debug
CONFIG = qt warn_on release
-HEADERS = portabase.h inputdialog.h view.h viewdisplay.h database.h datatypes.h dbeditor.h columneditor.h roweditor.h csvutils.h importdialog.h vieweditor.h noteeditor.h notebutton.h
-SOURCES = main.cpp portabase.cpp inputdialog.cpp view.cpp viewdisplay.cpp database.cpp dbeditor.cpp columneditor.cpp roweditor.cpp csvutils.cpp importdialog.cpp vieweditor.cpp noteeditor.cpp notebutton.cpp
+HEADERS = portabase.h inputdialog.h view.h viewdisplay.h database.h datatypes.h dbeditor.h columneditor.h roweditor.h csvutils.h importdialog.h vieweditor.h noteeditor.h notebutton.h sorteditor.h
+SOURCES = main.cpp portabase.cpp inputdialog.cpp view.cpp viewdisplay.cpp database.cpp dbeditor.cpp columneditor.cpp roweditor.cpp csvutils.cpp importdialog.cpp vieweditor.cpp noteeditor.cpp notebutton.cpp sorteditor.cpp
INCLUDEPATH += $(QPEDIR)/include
DEPENDPATH += $(QPEDIR)/include
LIBS += -lqpe -lm -lmk4
diff --git a/sorteditor.cpp b/sorteditor.cpp
new file mode 100644
index 0000000..08d9fc3
--- /dev/null
+++ b/sorteditor.cpp
@@ -0,0 +1,273 @@
+/*
+ * sorteditor.cpp
+ *
+ * (c) 2002 by Jeremy Bowman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "database.h"
+#include "sorteditor.h"
+
+SortEditor::SortEditor(QWidget *parent, const char *name, WFlags f)
+ : QDialog(parent, name, TRUE, f), db(0)
+{
+ setCaption(tr("Sorting Editor") + " - " + tr("PortaBase"));
+ showMaximized();
+ hide();
+ QVBox *vbox = new QVBox(this);
+ vbox->resize(size());
+
+ QHBox *hbox = new QHBox(vbox);
+ new QLabel(tr("Sorting Name"), hbox);
+ nameBox = new QLineEdit(hbox);
+
+ table = new QListView(vbox);
+ table->setAllColumnsShowFocus(TRUE);
+ table->setSorting(-1);
+ table->header()->setMovingEnabled(FALSE);
+ table->addColumn(tr("Sort"));
+ table->setColumnWidthMode(0, QListView::Manual);
+ table->setColumnAlignment(0, Qt::AlignHCenter);
+ int colWidth = (vbox->width() - table->columnWidth(0) - 5) / 2;
+ table->addColumn(tr("Column Name"), colWidth);
+ table->addColumn(tr("Direction"), colWidth);
+ connect(table, SIGNAL(clicked(QListViewItem*, const QPoint&, int)),
+ this, SLOT(tableClicked(QListViewItem*, const QPoint&, int)));
+
+ hbox = new QHBox(vbox);
+ QPushButton *upButton = new QPushButton(tr("Up"), hbox);
+ connect(upButton, SIGNAL(clicked()), this, SLOT(moveUp()));
+ QPushButton *downButton = new QPushButton(tr("Down"), hbox);
+ connect(downButton, SIGNAL(clicked()), this, SLOT(moveDown()));
+}
+
+SortEditor::~SortEditor()
+{
+
+}
+
+int SortEditor::edit(Database *subject, QString sortingName)
+{
+ db = subject;
+ originalName = sortingName;
+ nameBox->setText(sortingName);
+ colNames = db->listColumns();
+ db->getSortingInfo(sortingName, &sortCols, &descCols);
+ // move currently sorted columns to the top of the list, in correct order
+ int count = sortCols.count();
+ for (int i = count - 1; i > -1; i--) {
+ QString name = sortCols[i];
+ colNames.remove(name);
+ colNames.prepend(name);
+ }
+ updateTable();
+ int result = exec();
+ while (result) {
+ if (hasValidName()) {
+ break;
+ }
+ else {
+ result = exec();
+ }
+ }
+ return result;
+}
+
+QString SortEditor::getName()
+{
+ return nameBox->text();
+}
+
+void SortEditor::moveUp()
+{
+ QListViewItem *item = table->selectedItem();
+ if (item == 0) {
+ return;
+ }
+ QListViewItem *above = item->itemAbove();
+ if (above) {
+ QString name = item->text(1);
+ QString aboveName = above->text(1);
+ colNames.remove(name);
+ colNames.insert(colNames.find(aboveName), name);
+ updateTable();
+ selectRow(name);
+ }
+}
+
+void SortEditor::moveDown()
+{
+ QListViewItem *item = table->selectedItem();
+ if (item == 0) {
+ return;
+ }
+ QListViewItem *below = item->itemBelow();
+ if (below) {
+ QString name = item->text(1);
+ colNames.remove(name);
+ below = below->itemBelow();
+ if (below) {
+ QString belowName = below->text(1);
+ colNames.insert(colNames.find(belowName), name);
+ }
+ else {
+ colNames.append(name);
+ }
+ updateTable();
+ selectRow(name);
+ }
+}
+
+void SortEditor::selectRow(QString name)
+{
+ QListViewItem *item = table->firstChild();
+ if (item) {
+ if (item->text(1) == name) {
+ table->setSelected(item, TRUE);
+ }
+ else {
+ QListViewItem *next = item->nextSibling();
+ while (next) {
+ if (next->text(1) == name) {
+ table->setSelected(next, TRUE);
+ break;
+ }
+ next = next->nextSibling();
+ }
+ }
+ }
+}
+
+void SortEditor::updateTable()
+{
+ table->clear();
+ int count = colNames.count();
+ QListViewItem *item = 0;
+ for (int i = 0; i < count; i++) {
+ QString name = colNames[i];
+ bool sorted = isSorted(name);
+ QString direction = "";
+ if (sorted) {
+ if (descCols.findIndex(name) == -1) {
+ direction = tr("Ascending");
+ }
+ else {
+ direction = tr("Descending");
+ }
+ }
+ if (i == 0) {
+ item = new QListViewItem(table, "", name, direction);
+ }
+ else {
+ item = new QListViewItem(table, item, "", name, direction);
+ }
+ item->setPixmap(0, db->getCheckBoxPixmap(sorted));
+ }
+}
+
+void SortEditor::tableClicked(QListViewItem *item, const QPoint &point,
+ int column)
+{
+ if (column == 0) {
+ QString name = item->text(1);
+ int sorted = isSorted(name);
+ if (sorted) {
+ sortCols.remove(name);
+ descCols.remove(name);
+ item->setText(2, "");
+ }
+ else {
+ sortCols.append(name);
+ item->setText(2, tr("Ascending"));
+ }
+ item->setPixmap(0, db->getCheckBoxPixmap(!sorted));
+ }
+ else if (column == 2) {
+ QString name = item->text(1);
+ int sorted = isSorted(name);
+ if (sorted) {
+ if (descCols.findIndex(name) == -1) {
+ descCols.append(name);
+ item->setText(2, tr("Descending"));
+ }
+ else {
+ descCols.remove(name);
+ item->setText(2, tr("Ascending"));
+ }
+ }
+ }
+}
+
+bool SortEditor::hasValidName()
+{
+ QString name = nameBox->text();
+ if (name.isEmpty()) {
+ QMessageBox::warning(this, tr("PortaBase"),
+ tr("No name entered"));
+ return FALSE;
+ }
+ if (name == originalName) {
+ // hasn't changed and isn't empty, must be valid
+ return TRUE;
+ }
+ if (name[0] == '_') {
+ QMessageBox::warning(this, tr("PortaBase"),
+ tr("Name must not start with '_'"));
+ return FALSE;
+ }
+ if (name.find(':') != -1) {
+ QMessageBox::warning(this, tr("PortaBase"),
+ tr("Name must not contain ':'"));
+ return FALSE;
+ }
+ // check for other views with same name
+ bool result = TRUE;
+ QStringList sortingNames = db->listSortings();
+ int count = sortingNames.count();
+ for (int i = 0; i < count; i++) {
+ if (name == sortingNames[i]) {
+ result = FALSE;
+ break;
+ }
+ }
+ if (!result) {
+ QMessageBox::warning(this, tr("PortaBase"), tr("Duplicate name"));
+ }
+ return result;
+}
+
+int SortEditor::isSorted(QString name)
+{
+ return (sortCols.findIndex(name) != -1);
+}
+
+void SortEditor::applyChanges()
+{
+ QString sortingName = nameBox->text();
+ QStringList orderedSort;
+ QStringList orderedDesc;
+ int count = colNames.count();
+ for (int i = 0; i < count; i++) {
+ QString name = colNames[i];
+ if (isSorted(name)) {
+ orderedSort.append(name);
+ if (descCols.findIndex(name) != -1) {
+ orderedDesc.append(name);
+ }
+ }
+ }
+ db->deleteSorting(originalName);
+ db->addSorting(sortingName, orderedSort, orderedDesc);
+}
diff --git a/sorteditor.h b/sorteditor.h
new file mode 100644
index 0000000..9b53832
--- /dev/null
+++ b/sorteditor.h
@@ -0,0 +1,55 @@
+/*
+ * sorteditor.h
+ *
+ * (c) 2002 by Jeremy Bowman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef SORTEDITOR_H
+#define SORTEDITOR_H
+
+#include
+
+class Database;
+class QLineEdit;
+class QListView;
+class QListViewItem;
+class QPoint;
+
+class SortEditor: public QDialog
+{
+ Q_OBJECT
+public:
+ SortEditor(QWidget *parent = 0, const char *name = 0, WFlags f = 0);
+ ~SortEditor();
+
+ int edit(Database *subject, QString sortingName);
+ void applyChanges();
+ QString getName();
+
+private:
+ void updateTable();
+ void selectRow(QString name);
+ int isSorted(QString name);
+ bool hasValidName();
+
+private slots:
+ void tableClicked(QListViewItem *item, const QPoint &point, int column);
+ void moveUp();
+ void moveDown();
+
+private:
+ QLineEdit *nameBox;
+ QListView *table;
+ Database *db;
+ QString originalName;
+ QStringList colNames;
+ QStringList sortCols;
+ QStringList descCols;
+};
+
+#endif
diff --git a/view.cpp b/view.cpp
index 6a21750..ad3c8fa 100644
--- a/view.cpp
+++ b/view.cpp
@@ -19,7 +19,7 @@
View::View(Database *parent, c4_View baseview, QStringList colNames,
int *types, int *widths, int rpp) : Id("_id"), sortColumn(-1),
- ascending(TRUE)
+ ascending(TRUE), sortName("")
{
db = parent;
dbview = baseview;
@@ -28,6 +28,7 @@ View::View(Database *parent, c4_View baseview, QStringList colNames,
colWidths = widths;
numCols = columns.count();
rowsPerPage = rpp;
+ sort(db->currentSorting());
}
View::~View()
@@ -112,11 +113,20 @@ void View::sort(int colIndex)
}
}
+void View::sort(QString sortingName)
+{
+ sortColumn = -1;
+ sortName = sortingName;
+}
+
void View::prepareData()
{
if (sortColumn != -1) {
dbview = db->sortData(columns[sortColumn], ascending);
}
+ else if (sortName != "") {
+ dbview = db->sortData(sortName);
+ }
}
int View::getId(int index)
diff --git a/view.h b/view.h
index 4bec566..b388969 100644
--- a/view.h
+++ b/view.h
@@ -37,6 +37,7 @@ class View
QStringList getRow(int index);
int getId(int index);
void sort(int colIndex);
+ void sort(QString sortingName);
void prepareData();
QStringList getStatistics(int colIndex);
@@ -51,6 +52,7 @@ class View
int rowsPerPage;
int sortColumn;
bool ascending;
+ QString sortName;
};
#endif
diff --git a/viewdisplay.cpp b/viewdisplay.cpp
index cef4c27..bfcffcb 100644
--- a/viewdisplay.cpp
+++ b/viewdisplay.cpp
@@ -237,6 +237,12 @@ void ViewDisplay::setView(QString name)
updateButtons();
}
+void ViewDisplay::setSorting(QString name)
+{
+ view->sort(name);
+ updateTable();
+}
+
void ViewDisplay::closeView()
{
if (view) {
@@ -367,6 +373,7 @@ void ViewDisplay::sort(int column)
{
view->sort(column);
updateTable();
+ portabase->updateSortMenu();
}
void ViewDisplay::showStatistics(int column)
diff --git a/viewdisplay.h b/viewdisplay.h
index 9b7c956..fd6e5ea 100644
--- a/viewdisplay.h
+++ b/viewdisplay.h
@@ -43,6 +43,7 @@ class ViewDisplay: public QVBox
void updateButtons();
void saveViewSettings();
void setView(QString name);
+ void setSorting(QString name);
void closeView();
public slots: