Просмотр исходного кода

Merge branch 'master' of https://devel.mephi.ru/dyokunev/tasks

Dmitry Yu Okunev лет назад: 8
Родитель
Сommit
3c715cdbae

+ 1 - 1
android/AndroidManifest.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0"?>
-<manifest package="org.qtproject.mephiTasks" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="0.4" android:versionCode="4" android:installLocation="auto">
+<manifest package="org.qtproject.mephiTasks" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="0.6" android:versionCode="6" android:installLocation="auto">
     <application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --" android:icon="@drawable/icon">
 	<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="unspecified" android:launchMode="singleTop">
 	    <intent-filter>

+ 14 - 0
common.h

@@ -24,9 +24,23 @@ struct settings {
 	QString apiKey;
 	QString issuesFilter;
 	enum mode mode;
+	bool hideOnStart;
 };
 extern struct settings settings;
 extern void loadSettings();
 extern void saveSettings();
 
+bool issueCmpFunct_statusIsClosed_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b );
+bool issueCmpFunct_statusPosition_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b );
+bool issueCmpFunct_statusPosition_gt ( const QJsonObject &issue_a, const QJsonObject &issue_b );
+bool issueCmpFunct_updatedOn_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b );
+bool issueCmpFunct_updatedOn_gt ( const QJsonObject &issue_a, const QJsonObject &issue_b );
+bool issueCmpFunct_name_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b );
+bool issueCmpFunct_name_gt ( const QJsonObject &issue_a, const QJsonObject &issue_b );
+bool issueCmpFunct_assignee_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b );
+bool issueCmpFunct_assignee_gt ( const QJsonObject &issue_a, const QJsonObject &issue_b );
+bool issueCmpFunct_dueTo_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b );
+bool issueCmpFunct_dueTo_gt ( const QJsonObject &issue_a, const QJsonObject &issue_b );
+bool timeEntryCmpFunct_from_lt ( const QJsonObject &timeEntry_a, const QJsonObject &timeEntry_b );
+
 #endif // COMMON_H

+ 10 - 4
logtimewindow.cpp

@@ -16,13 +16,19 @@ LogTimeWindow::LogTimeWindow ( QWidget *parent ) :
 
 	this->timeEntry.setRedmine ( redmine );
 
+	QTime currentTime      = QTime::currentTime();
+	QTime initialSinceTime = QTime::fromString ( "09:00", "hh':'mm" );
+	if (initialSinceTime > currentTime) {
+		initialSinceTime = currentTime;
+	}
+
 	this->ui->untilInput->setDate ( QDate::currentDate() );
 	this->ui->sinceInput->setDate ( QDate::currentDate() );
-	this->ui->untilInput->setTime ( QTime::currentTime() );
-	this->ui->sinceInput->setTime ( QTime::fromString ( "09:00", "hh':'mm" ) );
+	this->ui->untilInput->setTime ( currentTime );
+	this->ui->sinceInput->setTime ( initialSinceTime );
 	this->setWindowTitle ( "Система «Задачи» НИЯУ МИФИ: Учёт времени" );
 	connect ( redmine, SIGNAL ( callback_call       ( void*, callback_t, QNetworkReply*, QJsonDocument*, void* ) ),
-	      this,    SLOT   ( callback_dispatcher ( void*, callback_t, QNetworkReply*, QJsonDocument*, void* ) ) );
+		  this,    SLOT   ( callback_dispatcher ( void*, callback_t, QNetworkReply*, QJsonDocument*, void* ) ) );
 	this->selected_project_id = 0;
 	this->timeEntry.setProjectId ( redmine->myProject() ["id"].toInt() );
 	this->updateLastLogTime();
@@ -41,7 +47,7 @@ LogTimeWindow::~LogTimeWindow()
 void LogTimeWindow::on_saveSuccess()
 {
 	qDebug ( "LogTimeWindow::on_saveSuccess()" );
-	redmine->get_time_entries ( NULL, NULL, NULL, false, "user_id=me&limit=1" ); // Just to update the cache
+    redmine->get_time_entries ( ( void * ) NULL, NULL, NULL, false, "user_id=me&limit=1" ); // Just to update the cache
 	delete this;
 }
 

+ 87 - 2
main.cpp

@@ -42,6 +42,7 @@ void loadSettings()
 	QSettings qsettings ( settings.settingsFilePath, QSettings::IniFormat );
 	settings.apiKey       = qsettings.value ( "apiKey" ).toString();
 	settings.issuesFilter = qsettings.value ( "issuesFilter" ).toString();
+	settings.hideOnStart  = qsettings.value ( "hideOnStart" ).toBool();
 	QString mode          = qsettings.value ( "mode" ).toString();
 
 	if ( mode == "rector" ) {
@@ -53,12 +54,94 @@ void loadSettings()
 	return;
 }
 
+
+bool issueCmpFunct_statusIsClosed_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
+{
+	int issue_statusIsClosed_a = redmine->get_issue_status ( issue_a["status"].toObject() ["id"].toInt() ) ["is_closed"].toBool();
+	int issue_statusIsClosed_b = redmine->get_issue_status ( issue_b["status"].toObject() ["id"].toInt() ) ["is_closed"].toBool();
+	return issue_statusIsClosed_a < issue_statusIsClosed_b;
+}
+
+
+bool issueCmpFunct_statusPosition_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
+{
+	int issue_statusPosition_a = redmine->get_issue_status ( issue_a["status"].toObject() ["id"].toInt() ) ["position"].toInt();
+	int issue_statusPosition_b = redmine->get_issue_status ( issue_b["status"].toObject() ["id"].toInt() ) ["position"].toInt();
+	return issue_statusPosition_a < issue_statusPosition_b;
+}
+bool issueCmpFunct_statusPosition_gt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
+{
+	int issue_statusPosition_a = redmine->get_issue_status ( issue_a["status"].toObject() ["id"].toInt() ) ["position"].toInt();
+	int issue_statusPosition_b = redmine->get_issue_status ( issue_b["status"].toObject() ["id"].toInt() ) ["position"].toInt();
+	return issue_statusPosition_a > issue_statusPosition_b;
+}
+
+bool issueCmpFunct_updatedOn_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
+{
+	QString issue_updatedOn_a = issue_a["updated_on"].toString();
+	QString issue_updatedOn_b = issue_b["updated_on"].toString();
+	return issue_updatedOn_a < issue_updatedOn_b;
+}
+bool issueCmpFunct_updatedOn_gt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
+{
+	QString issue_updatedOn_a = issue_a["updated_on"].toString();
+	QString issue_updatedOn_b = issue_b["updated_on"].toString();
+	return issue_updatedOn_a > issue_updatedOn_b;
+}
+
+bool issueCmpFunct_name_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
+{
+	QString issue_name_a = issue_a["name"].toString();
+	QString issue_name_b = issue_b["name"].toString();
+	return issue_name_a < issue_name_b;
+}
+bool issueCmpFunct_name_gt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
+{
+	QString issue_name_a = issue_a["name"].toString();
+	QString issue_name_b = issue_b["name"].toString();
+	return issue_name_a > issue_name_b;
+}
+
+bool issueCmpFunct_assignee_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
+{
+	QString issue_assignee_a = issue_a["assigned_to"].toObject() ["name"].toString();
+	QString issue_assignee_b = issue_b["assigned_to"].toObject() ["name"].toString();
+	return issue_assignee_a < issue_assignee_b;
+}
+bool issueCmpFunct_assignee_gt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
+{
+	QString issue_assignee_a = issue_a["assigned_to"].toObject() ["name"].toString();
+	QString issue_assignee_b = issue_b["assigned_to"].toObject() ["name"].toString();
+	return issue_assignee_a > issue_assignee_b;
+}
+
+bool issueCmpFunct_dueTo_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
+{
+	QString issue_dueTo_a = issue_a["due_date"].toString();
+	QString issue_dueTo_b = issue_b["due_date"].toString();
+	return issue_dueTo_a < issue_dueTo_b;
+}
+bool issueCmpFunct_dueTo_gt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
+{
+	QString issue_dueTo_a = issue_a["due_date"].toString();
+	QString issue_dueTo_b = issue_b["due_date"].toString();
+	return issue_dueTo_a > issue_dueTo_b;
+}
+
+bool timeEntryCmpFunct_from_lt ( const QJsonObject &timeEntry_a, const QJsonObject &timeEntry_b )
+{
+	QString timeEntry_from_a = timeEntry_a["from"].toString();
+	QString timeEntry_from_b = timeEntry_b["from"].toString();
+	return timeEntry_from_a < timeEntry_from_b;
+}
+
 void saveSettings()
 {
 	QSettings qsettings ( settings.settingsFilePath, QSettings::IniFormat );
 	qsettings.setValue ( "apiKey",        settings.apiKey );
 	qsettings.setValue ( "issuesFilter",  settings.issuesFilter );
 	qsettings.setValue ( "mode",         ( settings.mode == MODE_RECTOR ) ? "rector" : "full" );
+	qsettings.setValue ( "hideOnStart",   settings.hideOnStart );
 	return;
 }
 
@@ -114,7 +197,8 @@ int main ( int argc, char *argv[] )
 			case MODE_RECTOR: {
 					MainWindowRector w;
                     qDebug("Mode: RECTOR");
-                    w.show();
+                    if (!settings.hideOnStart)
+						w.show();
 					a.setQuitOnLastWindowClosed ( false );
 					rc = a.exec();
 					break;
@@ -123,7 +207,8 @@ int main ( int argc, char *argv[] )
 			default: {
 					MainWindowFull w;
                     qDebug("Mode: FULL");
-                    w.show();
+                    if (!settings.hideOnStart)
+						w.show();
 					a.setQuitOnLastWindowClosed ( false );
 					rc = a.exec();
 					break;

+ 38 - 72
mainwindow-common.cpp

@@ -22,78 +22,6 @@
 
 #include "mainwindow-common.h"
 
-bool issueCmpFunct_statusIsClosed_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
-{
-	int issue_statusIsClosed_a = redmine->get_issue_status ( issue_a["status"].toObject() ["id"].toInt() ) ["is_closed"].toBool();
-	int issue_statusIsClosed_b = redmine->get_issue_status ( issue_b["status"].toObject() ["id"].toInt() ) ["is_closed"].toBool();
-	return issue_statusIsClosed_a < issue_statusIsClosed_b;
-}
-
-
-bool issueCmpFunct_statusPosition_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
-{
-	int issue_statusPosition_a = redmine->get_issue_status ( issue_a["status"].toObject() ["id"].toInt() ) ["position"].toInt();
-	int issue_statusPosition_b = redmine->get_issue_status ( issue_b["status"].toObject() ["id"].toInt() ) ["position"].toInt();
-	return issue_statusPosition_a < issue_statusPosition_b;
-}
-bool issueCmpFunct_statusPosition_gt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
-{
-	int issue_statusPosition_a = redmine->get_issue_status ( issue_a["status"].toObject() ["id"].toInt() ) ["position"].toInt();
-	int issue_statusPosition_b = redmine->get_issue_status ( issue_b["status"].toObject() ["id"].toInt() ) ["position"].toInt();
-	return issue_statusPosition_a > issue_statusPosition_b;
-}
-
-bool issueCmpFunct_updatedOn_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
-{
-	QString issue_updatedOn_a = issue_a["updated_on"].toString();
-	QString issue_updatedOn_b = issue_b["updated_on"].toString();
-	return issue_updatedOn_a < issue_updatedOn_b;
-}
-bool issueCmpFunct_updatedOn_gt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
-{
-	QString issue_updatedOn_a = issue_a["updated_on"].toString();
-	QString issue_updatedOn_b = issue_b["updated_on"].toString();
-	return issue_updatedOn_a > issue_updatedOn_b;
-}
-
-bool issueCmpFunct_name_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
-{
-	QString issue_name_a = issue_a["name"].toString();
-	QString issue_name_b = issue_b["name"].toString();
-	return issue_name_a < issue_name_b;
-}
-bool issueCmpFunct_name_gt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
-{
-	QString issue_name_a = issue_a["name"].toString();
-	QString issue_name_b = issue_b["name"].toString();
-	return issue_name_a > issue_name_b;
-}
-
-bool issueCmpFunct_assignee_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
-{
-	QString issue_assignee_a = issue_a["assigned_to"].toObject() ["name"].toString();
-	QString issue_assignee_b = issue_b["assigned_to"].toObject() ["name"].toString();
-	return issue_assignee_a < issue_assignee_b;
-}
-bool issueCmpFunct_assignee_gt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
-{
-	QString issue_assignee_a = issue_a["assigned_to"].toObject() ["name"].toString();
-	QString issue_assignee_b = issue_b["assigned_to"].toObject() ["name"].toString();
-	return issue_assignee_a > issue_assignee_b;
-}
-
-bool issueCmpFunct_dueTo_lt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
-{
-	QString issue_dueTo_a = issue_a["due_date"].toString();
-	QString issue_dueTo_b = issue_b["due_date"].toString();
-	return issue_dueTo_a < issue_dueTo_b;
-}
-bool issueCmpFunct_dueTo_gt ( const QJsonObject &issue_a, const QJsonObject &issue_b )
-{
-	QString issue_dueTo_a = issue_a["due_date"].toString();
-	QString issue_dueTo_b = issue_b["due_date"].toString();
-	return issue_dueTo_a > issue_dueTo_b;
-}
 
 MainWindowCommon::MainWindowCommon ( QWidget *parent ) :
 	QMainWindow ( parent )
@@ -111,6 +39,9 @@ MainWindowCommon::MainWindowCommon ( QWidget *parent ) :
 	this->sortFunctMap.insert ( SORT_UPDATED_ON_ASC,      issueCmpFunct_updatedOn_lt );
 	this->sortFunctMap.insert ( SORT_UPDATED_ON_DESC,     issueCmpFunct_updatedOn_gt );
 	this->sortFunctMap.insert ( SORT_STATUS_ISCLOSED_ASC, issueCmpFunct_statusIsClosed_lt );
+
+	this->sortFunctMap.insert ( SORT_TIMEENTRY_FROM_ASC,  timeEntryCmpFunct_from_lt );
+
 	memset ( this->sortColumn, 0, sizeof ( this->sortColumn ) );
 	this->sortColumn[0] = ( MainWindowCommon::ESortColumn ) qsettings.value ( "sortcode"   ).toInt();
 	this->sortLogicalIndex =                                qsettings.value ( "sortcolumn" ).toInt();
@@ -302,6 +233,28 @@ void MainWindowCommon::openLogTimeWindow()
 	return;
 }
 
+
+void MainWindowCommon::on_closeShowTimeWindow()
+{
+	this->showTimeWindow = NULL;
+
+	return;
+}
+
+void MainWindowCommon::openShowTimeWindow()
+{
+	if (this->showTimeWindow != NULL)
+		delete this->showTimeWindow;
+	this->showTimeWindow = new ShowTimeWindow;
+
+	connect ( this->showTimeWindow, SIGNAL ( on_destructor() ), this, SLOT ( on_closeShowTimeWindow() ) );
+
+	this->showTimeWindow->show();
+
+	return;
+}
+
+
 void MainWindowCommon::showOnTop()
 {
 #ifdef Q_OS_WIN32
@@ -319,5 +272,18 @@ void MainWindowCommon::showOnTop()
 	return;
 }
 
+void MainWindowCommon::toggleShowHide()
+{
+	if ( this->isVisible() ) {
+		settings.hideOnStart = true;
+		this->hide();
+	} else {
+		settings.hideOnStart = false;
+		this->showOnTop();
+	}
+
+	return;
+}
+
 
 /**** /tray-related stuff ****/

+ 10 - 1
mainwindow-common.h

@@ -11,6 +11,7 @@
 #include "memberships.h"
 #include "enumerations.h"
 #include "logtimewindow.h"
+#include "showtimewindow.h"
 
 #include "common.h"
 
@@ -75,6 +76,8 @@ protected:
 		SORT_UPDATED_ON_DESC,
 
 		SORT_STATUS_ISCLOSED_ASC,
+
+		SORT_TIMEENTRY_FROM_ASC,
 	};
 
 	typedef bool ( *sortfunct_t ) ( const QJsonObject &issue_a, const QJsonObject &issue_b );
@@ -115,9 +118,14 @@ protected slots:
 	int updateRoles();
 	void openLogTimeWindow();
 	void on_closeLogTimeWindow();
+	void openShowTimeWindow();
+	void on_closeShowTimeWindow();
 
 signals:
 
+protected slots:
+	void toggleShowHide();
+
 public slots:
 
 private:
@@ -137,7 +145,8 @@ private:
 
 
 	EStatus _status;
-	LogTimeWindow *logTimeWindow;
+	LogTimeWindow  *logTimeWindow;
+	ShowTimeWindow *showTimeWindow;
 };
 
 #endif // MAINWINDOWCOMMON_H

+ 4 - 11
mainwindow-full.cpp

@@ -1105,11 +1105,15 @@ void MainWindowFull::createTrayActions()
 	this->openLogTimeWindowAction = new QAction ( tr ( "Зажурналировать время" ), this );
 	connect ( this->openLogTimeWindowAction, SIGNAL ( triggered() ), this, SLOT ( openLogTimeWindow() ) );
 
+	this->openShowTimeWindowAction = new QAction ( tr ( "Журнал времени" ), this );
+	connect ( this->openShowTimeWindowAction, SIGNAL ( triggered() ), this, SLOT ( openShowTimeWindow() ) );
+
 	this->quitAction = new QAction ( tr ( "Завершить" ), this );
 	connect ( this->quitAction, SIGNAL ( triggered() ), qApp, SLOT ( quit() ) );
 
 	this->trayIconMenu->addAction ( this->showHideAction );
 	this->trayIconMenu->addAction ( this->openLogTimeWindowAction );
+	this->trayIconMenu->addAction ( this->openShowTimeWindowAction );
 	this->trayIconMenu->addAction ( this->quitAction );
 
 	return;
@@ -1135,15 +1139,4 @@ void MainWindowFull::iconActivated ( QSystemTrayIcon::ActivationReason reason )
 }
 
 
-void MainWindowFull::toggleShowHide()
-{
-	if ( this->isVisible() )
-		this->hide();
-	else {
-		this->showOnTop();
-	}
-
-	return;
-}
-
 /**** /tray-related stuff ****/

+ 2 - 1
mainwindow-full.h

@@ -35,7 +35,7 @@ protected:
 	bool eventFilter ( QObject *obj, QEvent *event );
 
 protected slots:
-	void toggleShowHide();
+
 
 private slots:
 	void on_actionHelp_triggered();
@@ -161,6 +161,7 @@ private:
 	QAction *showHideAction;
 	QAction *quitAction;
 	QAction *openLogTimeWindowAction;
+	QAction *openShowTimeWindowAction;
 
 	void iconActivated ( QSystemTrayIcon::ActivationReason reason );
 };

+ 0 - 11
mainwindow-rector.cpp

@@ -350,17 +350,6 @@ void MainWindowRector::on_actionHelp_triggered()
 	return;
 }
 
-void MainWindowRector::toggleShowHide()
-{
-	if ( this->isVisible() )
-		this->hide();
-	else {
-		this->showOnTop();
-	}
-
-	return;
-}
-
 void MainWindowRector::createTrayActions()
 {
 	this->showHideAction = new QAction ( tr ( "Показать/Спрятать" ), this );

+ 0 - 1
mainwindow-rector.h

@@ -30,7 +30,6 @@ public:
 	~MainWindowRector();
 
 protected slots:
-	void toggleShowHide();
 
 private slots:
 	void on_actionExit_triggered();

+ 7 - 0
mainwindowandroid.cpp

@@ -2,6 +2,7 @@
 #include "ui_mainwindowandroid.h"
 
 #include "logtimewindow.h"
+#include "showtimewindow.h"
 
 MainWindowAndroid::MainWindowAndroid ( QWidget *parent ) :
 	QFrame ( parent ),
@@ -26,3 +27,9 @@ void MainWindowAndroid::on_logTimeWindowButton_clicked()
 	LogTimeWindow *w = new LogTimeWindow();
 	w->show();
 }
+
+void MainWindowAndroid::on_showTimeWindowButton_clicked()
+{
+	ShowTimeWindow *w = new ShowTimeWindow();
+	w->show();
+}

+ 3 - 1
mainwindowandroid.h

@@ -21,7 +21,9 @@ private slots:
 
 	void on_logTimeWindowButton_clicked();
 
-private:
+	void on_showTimeWindowButton_clicked();
+
+	private:
 	Ui::MainWindowAndroid *ui;
 };
 

+ 7 - 0
mainwindowandroid.ui

@@ -36,6 +36,13 @@
       </widget>
      </item>
      <item>
+      <widget class="QPushButton" name="showTimeWindowButton">
+       <property name="text">
+        <string>Посмотреть журнал</string>
+       </property>
+      </widget>
+     </item>
+     <item>
       <widget class="QPushButton" name="quitButton">
        <property name="text">
         <string>Выйти</string>

+ 6 - 3
mephi-tasks.pro

@@ -31,7 +31,8 @@ SOURCES += main.cpp\
     loginwindow.cpp \
     mainwindowandroid.cpp \
     logtimewindow.cpp \
-    redmineclass_time_entry.cpp
+    redmineclass_time_entry.cpp \
+    showtimewindow.cpp
 
 HEADERS  += \
     helpwindow.h \
@@ -52,7 +53,8 @@ HEADERS  += \
     loginwindow.h \
     mainwindowandroid.h \
     logtimewindow.h \
-    redmineclass_time_entry.h
+    redmineclass_time_entry.h \
+    showtimewindow.h
 
 FORMS    += \
     helpwindow.ui \
@@ -63,7 +65,8 @@ FORMS    += \
     signingwindow.ui \
     loginwindow.ui \
     mainwindowandroid.ui \
-    logtimewindow.ui
+    logtimewindow.ui \
+    showtimewindow.ui
 
 win32:CONFIG(release, debug|release): LIBS += -L$$PWD/build-qtredmine/release/ -lqtredmine
 else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/build-qtredmine/debug/ -lqtredmine

+ 1 - 1
qtredmine

@@ -1 +1 @@
-Subproject commit c4b5c00a6965e28015e286b85328d7e88459fed4
+Subproject commit d564fb78c78f0354d8dc297a22843ad06a6354c7

+ 41 - 0
redmine.cpp

@@ -500,9 +500,19 @@ QNetworkReply *Redmine::get_time_entries ( void *obj_ptr, callback_t callback,
 QNetworkReply *Redmine::get_time_entries ( callback_t callback,
         void *arg, bool free_arg, QString filterOptions )
 {
+	qDebug("deprecated variant of Redmine::get_time_entries had been called");
+
 	return this->get_time_entries ( NULL, callback, arg, free_arg, filterOptions );
 }
 
+QNetworkReply *Redmine::get_time_entries ( int userId, void *obj_ptr, callback_t callback,
+	void *arg, bool free_arg, QString filterOptions )
+{
+	QString userId_str = ( userId == 0 ? "" : QString ( "user_id=" + QString::number(userId) ) );
+
+	return this->get_time_entries ( obj_ptr, callback, arg, free_arg, userId_str+"&"+filterOptions );
+}
+
 /********* /get_time_entries *********/
 
 /********* get_projects *********/
@@ -581,14 +591,23 @@ QNetworkReply *Redmine::get_user ( int user_id,
 
 QDateTime Redmine::parseDateTime ( QString date_str )
 {
+	qDebug ( "Used deprecated function Redmine::parseDateTime(). Use \"QDateTime::fromString (arg, Qt::ISODate)\" instead." );
+
 	// TODO: FIXME: make this function working on any timezone.
 	QDateTime date;
 	date = QDateTime::fromString ( date_str, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'zzz'+03:00'" );
 
 	if ( !date.isValid() )
+		date = QDateTime::fromString ( date_str, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'zzz'+0300'" );
+
+	if ( !date.isValid() )
 		// TODO: FIXME: add a hour
 		date = QDateTime::fromString ( date_str, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'zzz'+04:00'" );
 
+	if ( !date.isValid() )
+		// TODO: FIXME: add a hour
+		date = QDateTime::fromString ( date_str, "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'zzz'+0400'" );
+
 	return date;
 }
 
@@ -607,3 +626,25 @@ QUrl Redmine::getUrl ( QString objectType, int objectId )
 }
 
 /********* /getUrl *********/
+
+/********* get_stuff_to_do *********/
+
+QNetworkReply *Redmine::get_stuff_to_do ( void *obj_ptr, callback_t callback,
+				 int user_id,
+				 void *arg, bool free_arg,
+				 QString filterOptions )
+{
+	QString user_id_str = ( user_id == 0 ? "" : "user_id=" + QString::number ( user_id ) );
+
+	return this->request(
+				GET,
+				"stuff_to_do",
+				obj_ptr,
+				callback,
+				arg,
+				free_arg,
+				user_id_str+"&"+filterOptions );
+}
+
+/********* /get_stuff_to_do *********/
+

+ 21 - 10
redmine.h

@@ -100,30 +100,31 @@ public:
 
 	/* Request all roles
 	 */
-	QNetworkReply *get_roles ( callback_t callback, void *arg, bool free_arg = false );
+	QNetworkReply *get_roles ( callback_t callback, void *arg = NULL, bool free_arg = false );
 
 	/* Request all issues
 	 */
-	QNetworkReply *get_issues ( callback_t callback, void *arg, bool free_arg = false, QString customFilters = "" );
-	QNetworkReply *get_issues ( void *obj_ptr, callback_t callback, void *arg, bool free_arg = false, QString customFilters = "" );
+	QNetworkReply *get_issues ( callback_t callback, void *arg = NULL, bool free_arg = false, QString customFilters = "" );
+	QNetworkReply *get_issues ( void *obj_ptr, callback_t callback, void *arg = NULL, bool free_arg = false, QString customFilters = "" );
 
 	/* Request all projects
 	 */
-	QNetworkReply *get_projects ( callback_t callback, void *arg, bool free_arg = false, QString filterOptions = "" );
-	QNetworkReply *get_projects ( void *obj_ptr, callback_t callback, void *arg, bool free_arg = false, QString filterOptions = "" );
+	QNetworkReply *get_projects ( callback_t callback, void *arg = NULL, bool free_arg = false, QString filterOptions = "" );
+	QNetworkReply *get_projects ( void *obj_ptr, callback_t callback, void *arg = NULL, bool free_arg = false, QString filterOptions = "" );
 
 	/* Request all memberships
 	 */
-	QNetworkReply *get_memberships ( callback_t callback, void *arg, bool free_arg = false );
+	QNetworkReply *get_memberships ( callback_t callback, void *arg = NULL, bool free_arg = false );
 
 	/* Request all field values enumerations
 	 */
-	QNetworkReply *get_enumerations ( callback_t callback, void *arg, bool free_arg = false );
+	QNetworkReply *get_enumerations ( callback_t callback, void *arg = NULL, bool free_arg = false );
 
 	/* Request all issues
 	 */
-	QNetworkReply *get_time_entries ( void *obj_ptr, callback_t callback, void *arg, bool free_arg = false, QString filterOptions = "" );
-	QNetworkReply *get_time_entries ( callback_t callback, void *arg, bool free_arg = false, QString filterOptions = "" );
+	QNetworkReply *get_time_entries ( int userId, void *obj_ptr, callback_t callback, void *arg = NULL, bool free_arg = false, QString filterOptions = "" );
+	QNetworkReply *get_time_entries ( void *obj_ptr, callback_t callback, void *arg = NULL, bool free_arg = false, QString filterOptions = "" );
+	QNetworkReply *get_time_entries ( callback_t callback, void *arg = NULL, bool free_arg = false, QString filterOptions = "" );
 
 	/* Get issue status info
 	 */
@@ -136,7 +137,17 @@ public:
 	 */
 	QNetworkReply *get_user ( int user_id,
 	                          callback_t callback,
-	                          void *arg );
+				  void *arg = NULL );
+
+	/* Request stuffToDo index info of user with ID "user_id".
+	 * Set "user_id" to zero to get info for current user.
+	 *
+	 * Related to plugin: https://github.com/mephi-ut/stuff_to_do_plugin
+	 */
+	QNetworkReply *get_stuff_to_do ( void *obj_ptr, callback_t callback,
+					 int user_id = 0,
+					 void *arg = NULL, bool free_arg = false,
+					 QString filterOptions = "" );
 
 	/* Parses JSON values like "2015-03-13T21:34:28.000+03:00" to QDateTime
 	 */

+ 3 - 1
redmineclass_time_entry.cpp

@@ -7,6 +7,9 @@ void RedmineClass_TimeEntry::init()
 	this->redmine   = NULL;
 	this->id        = 0;
 	this->saveReply = NULL;
+
+	connect ( &this->saveTimer, SIGNAL ( timeout() ), this, SLOT ( saveTimeout() ) );
+
 	return;
 }
 
@@ -143,7 +146,6 @@ int RedmineClass_TimeEntry::save()
 	this->saveReply = this->redmine->request ( mode, "time_entries", this, (Redmine::callback_t)&RedmineClass_TimeEntry::saveCallback, NULL, false, "", timeEntries );
 
 	this->saveTimer.setSingleShot ( true );
-	connect ( &this->saveTimer, SIGNAL ( timeout() ), this, SLOT ( saveTimeout() ) );
 	this->saveTimer.start ( REDMINE_BASETIMEOUT );
 
 	return 0;

+ 228 - 0
showtimewindow.cpp

@@ -0,0 +1,228 @@
+#include "showtimewindow.h"
+#include "ui_showtimewindow.h"
+
+ShowTimeWindow::ShowTimeWindow(QWidget *parent) :
+	QWidget(parent),
+	ui(new Ui::ShowTimeWindow)
+{
+	ui->setupUi(this);
+
+	connect ( redmine, SIGNAL ( callback_call       ( void*, callback_t, QNetworkReply*, QJsonDocument*, void* ) ),
+		  this,    SLOT   ( callback_dispatcher ( void*, callback_t, QNetworkReply*, QJsonDocument*, void* ) ) );
+
+	this->updateUsers();
+	this->updateTimeEntries(0);
+
+	this->ui->timeEntries->horizontalHeader()->setSectionResizeMode ( 0, QHeaderView::Interactive );
+	this->ui->timeEntries->horizontalHeader()->setSectionResizeMode ( 1, QHeaderView::Interactive );
+	this->ui->timeEntries->horizontalHeader()->setSectionResizeMode ( 2, QHeaderView::Interactive );
+	this->ui->timeEntries->horizontalHeader()->setSectionResizeMode ( 3, QHeaderView::Interactive );
+	this->ui->timeEntries->horizontalHeader()->setSectionResizeMode ( 4, QHeaderView::Interactive );
+	this->ui->timeEntries->horizontalHeader()->setSectionResizeMode ( 5, QHeaderView::Interactive );
+
+#ifdef __ANDROID__
+	this->ui->timeEntries->horizontalHeader()->setSectionResizeMode ( 6, QHeaderView::Interactive );
+
+	this->ui->timeEntries->horizontalHeader()->resizeSection ( 0, 80 );
+	this->ui->timeEntries->horizontalHeader()->resizeSection ( 1, 80 );
+	this->ui->timeEntries->horizontalHeader()->resizeSection ( 2, 72 );
+	this->ui->timeEntries->horizontalHeader()->resizeSection ( 3, 104 );
+	this->ui->timeEntries->horizontalHeader()->resizeSection ( 4, 82 );
+	this->ui->timeEntries->horizontalHeader()->resizeSection ( 5, 500 );
+	this->ui->timeEntries->horizontalHeader()->resizeSection ( 6, 500 );
+#else
+	this->ui->timeEntries->horizontalHeader()->setSectionResizeMode ( 6, QHeaderView::Stretch );
+
+	this->ui->timeEntries->horizontalHeader()->resizeSection ( 0, 50 );
+	this->ui->timeEntries->horizontalHeader()->resizeSection ( 1, 50 );
+	this->ui->timeEntries->horizontalHeader()->resizeSection ( 2, 45 );
+	this->ui->timeEntries->horizontalHeader()->resizeSection ( 3, 70 );
+	this->ui->timeEntries->horizontalHeader()->resizeSection ( 4, 60 );
+	this->ui->timeEntries->horizontalHeader()->resizeSection ( 5, 200 );
+
+	this->ui->date->setMaximumHeight(120);
+#endif
+
+	this->ui->timeEntries->horizontalHeader()->setSortIndicatorShown ( true );
+
+	this->ui->date->setSelectedDate(QDate::currentDate());
+}
+
+ShowTimeWindow::~ShowTimeWindow()
+{
+	delete ui;
+}
+
+/********* updateUsers *********/
+
+
+void ShowTimeWindow::updateUsers_callback ( QNetworkReply *reply, QJsonDocument *json, void *arg )
+{
+	( void ) reply;
+	( void ) arg;
+
+	QJsonArray  users = json->object() ["users"].toArray();
+
+	qDebug ( "ShowTimeWindow::updateUsers_callback: users.count() == %i", users.count() );
+
+	this->ui->user->clear();
+	this->ui->user->addItem ( "Я", 0 );
+
+	foreach (const QJsonValue &userV, users) {
+		QJsonObject user = userV.toObject();
+
+		int userId = user["id"].toInt();
+		QString userDisplayName = user["name"].toString();
+		this->ui->user->addItem ( userDisplayName, userId );
+
+		// To make a cache:
+		redmine->get_time_entries( userId, NULL, NULL, NULL, false, "limit=1000" );
+	}
+
+	return;
+}
+
+void ShowTimeWindow::updateUsers()
+{
+	redmine->get_stuff_to_do ( this, ( Redmine::callback_t ) &ShowTimeWindow::updateUsers_callback );
+	return;
+}
+
+/********* /updateUsers *********/
+
+/********* updateTimeEntries *********/
+
+void ShowTimeWindow::timeEntries_display()
+{
+	QList<QJsonObject> list;
+	//this->ui->timeEntries->clear();
+	//this->ui->timeEntries->setRowCount ( this->timeEntries.count() );
+
+	QDate dateSelected = this->ui->date->selectedDate();
+
+	foreach (const QJsonValue &timeEntryV, this->timeEntries) {
+		QJsonObject timeEntry = timeEntryV.toObject();
+		QDateTime from = QDateTime::fromString(timeEntry ["from"].toString(), Qt::ISODate);
+
+		QDate date = from.date();
+
+		if (date != dateSelected)
+			continue;
+
+		list.append(timeEntry);
+	}
+
+	qSort ( list.begin(), list.end(), timeEntryCmpFunct_from_lt );
+
+	this->ui->timeEntries->setRowCount ( list.size() );
+
+	for (int row = 0; row < list.size(); ++row) {
+	    QJsonObject timeEntry = list.at(row);
+
+	    QDateTime from = QDateTime::fromString(timeEntry ["from"].toString(), Qt::ISODate);
+	    QDateTime to   = QDateTime::fromString(timeEntry ["to"  ].toString(), Qt::ISODate);
+	    int sec_diff  = from.time().secsTo(to.time());
+
+	    //qDebug ( "\"from\": \"%s\", sec_diff == %i; from.minute() == %i; to.minute() == %i", timeEntry ["from"].toString().toStdString().c_str(), sec_diff, from.time().minute(), to.time().minute() );
+
+	    this->ui->timeEntries->setItem(row, 0, new QTableWidgetItem ( from.toString("hh:mm") ) );
+	    this->ui->timeEntries->setItem(row, 1, new QTableWidgetItem ( to  .toString("hh:mm") ) );
+	    this->ui->timeEntries->setItem(row, 2, new QTableWidgetItem ( QString::number( sec_diff/60 ) ) );
+	    this->ui->timeEntries->setItem(row, 3, new QTableWidgetItem ( timeEntry ["activity"].toObject() ["name"].toString() ) );
+
+	    if (timeEntry ["issue"].toObject().empty()) {
+		    QString projectIdentifier = timeEntry ["project"].toObject() ["identifier"].toString();
+		    QString projectName;
+		    QString userLogin = timeEntry ["user"].toObject() ["login"].toString();
+
+		    if ( projectIdentifier.toLower() == userLogin.toLower() ) {
+			    projectName = "Личный проект";
+		    } else {
+			    projectName = timeEntry ["project"].toObject() ["name"].toString();
+		    }
+
+		    //qDebug(("projectIdentifier == \""+projectIdentifier+"\"; userLogin == \""+userLogin+"\"; projectName == \""+projectName+"\"").toStdString().c_str());
+
+		    this->ui->timeEntries->setItem(row, 4, new QTableWidgetItem ( projectIdentifier ) );
+		    this->ui->timeEntries->setItem(row, 5, new QTableWidgetItem ( projectName       ) );
+	    } else {
+		    this->ui->timeEntries->setItem(row, 4, new QTableWidgetItem ( QString::number(timeEntry ["issue"].toObject()    ["id"].toInt()) ) );
+		    this->ui->timeEntries->setItem(row, 5, new QTableWidgetItem ( timeEntry ["issue"].toObject()    ["subject"].toString() ) );
+	    }
+
+	    this->ui->timeEntries->setItem(row, 6, new QTableWidgetItem ( timeEntry ["comments"].toString() ) );
+	}
+}
+
+void ShowTimeWindow::updateTimeEntries_callback ( QNetworkReply *reply, QJsonDocument *json, void *arg )
+{
+	( void ) reply;
+	( void ) arg;
+
+	this->timeEntries = json->object() ["time_entries"].toArray();
+
+	qDebug ( "ShowTimeWindow::updateTimeEntries_callback: timeEntries.count() == %i", timeEntries.count() );
+
+	this->timeEntries_display();
+	return;
+}
+
+void ShowTimeWindow::updateTimeEntries(int userId)
+{
+	redmine->get_time_entries( userId, this, ( Redmine::callback_t ) &ShowTimeWindow::updateTimeEntries_callback, NULL, false, "limit=1000" );
+	return;
+}
+
+/********* /updateTimeEntries *********/
+
+void ShowTimeWindow::on_closeButton_clicked()
+{
+	delete this;
+}
+
+void ShowTimeWindow::on_date_selectionChanged()
+{
+	this->timeEntries_display();
+}
+
+void ShowTimeWindow::on_timeEntries_itemSelectionChanged()
+{
+	QTableWidget                     *timeEntries        = this->ui->timeEntries;
+	int                               columns_count      = timeEntries->columnCount();
+	int                               rows_count         = timeEntries->rowCount();
+	QList<QTableWidgetSelectionRange> selected_list      = timeEntries->selectedRanges();
+	foreach ( QTableWidgetSelectionRange range, selected_list ) {
+		if ( range.leftColumn() != 0 || range.rightColumn() != columns_count - 1 )
+			timeEntries->setRangeSelected (
+			    QTableWidgetSelectionRange (
+				range.topRow(),    0,
+				range.bottomRow(), columns_count - 1
+			    ),
+			    true
+			);
+		else
+
+			/* Workaround: Drop selection if everything is selected
+			 * it's required to do not select everything on sort switching
+			 */
+			if ( range.leftColumn() == 0 && range.rightColumn() == columns_count - 1 &&
+			     range.topRow()     == 0 && range.bottomRow()   == rows_count - 1 ) {
+				timeEntries->setRangeSelected (
+				    QTableWidgetSelectionRange ( 0, 0, rows_count - 1, columns_count - 1 ),
+				    false
+				);
+				break;
+			}
+	}
+}
+
+void ShowTimeWindow::on_user_currentIndexChanged(int index)
+{
+	(void) index;
+
+	int userId = this->ui->user->currentData().toInt();
+
+	this->updateTimeEntries(userId);
+
+	return;
+}

+ 46 - 0
showtimewindow.h

@@ -0,0 +1,46 @@
+#ifndef SHOWTIMEWINDOW_H
+#define SHOWTIMEWINDOW_H
+
+#include <QWidget>
+
+#include "common.h"
+#include "redmineitemtree.h"
+
+namespace Ui {
+	class ShowTimeWindow;
+}
+
+class ShowTimeWindow : public QWidget
+{
+	Q_OBJECT
+	CALLBACK_DISPATCHER ( Redmine, ShowTimeWindow, this )
+
+public:
+	explicit ShowTimeWindow(QWidget *parent = 0);
+	~ShowTimeWindow();
+
+	private slots:
+	void on_closeButton_clicked();
+
+	void on_date_selectionChanged();
+
+	void on_timeEntries_itemSelectionChanged();
+
+	void on_user_currentIndexChanged(int index);
+
+	private:
+	Ui::ShowTimeWindow *ui;
+
+	void updateUsers();
+	void updateTimeEntries ( int userId );
+	void updateUsers_callback       ( QNetworkReply *reply, QJsonDocument *json, void *arg );
+	void updateTimeEntries_callback ( QNetworkReply *reply, QJsonDocument *json, void *arg );
+
+	//RedmineItemTree users;
+	//RedmineItemTree timeEntries;
+	QJsonArray timeEntries;
+private:
+	void timeEntries_display();
+};
+
+#endif // SHOWTIMEWINDOW_H

+ 93 - 0
showtimewindow.ui

@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ShowTimeWindow</class>
+ <widget class="QWidget" name="ShowTimeWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>699</width>
+    <height>452</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout">
+   <item>
+    <layout class="QVBoxLayout" name="layout">
+     <item>
+      <widget class="QComboBox" name="user">
+       <item>
+        <property name="text">
+         <string>Я</string>
+        </property>
+       </item>
+      </widget>
+     </item>
+     <item>
+      <widget class="QCalendarWidget" name="date">
+       <property name="maximumSize">
+        <size>
+         <width>16777215</width>
+         <height>16777215</height>
+        </size>
+       </property>
+       <property name="locale">
+        <locale language="Russian" country="Russia"/>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QTableWidget" name="timeEntries">
+       <column>
+        <property name="text">
+         <string>с</string>
+        </property>
+       </column>
+       <column>
+        <property name="text">
+         <string>по</string>
+        </property>
+       </column>
+       <column>
+        <property name="text">
+         <string>мин.</string>
+        </property>
+       </column>
+       <column>
+        <property name="text">
+         <string>тип</string>
+        </property>
+       </column>
+       <column>
+        <property name="text">
+         <string>#</string>
+        </property>
+       </column>
+       <column>
+        <property name="text">
+         <string>задача/проект</string>
+        </property>
+       </column>
+       <column>
+        <property name="text">
+         <string>комментарий</string>
+        </property>
+       </column>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="closeButton">
+       <property name="text">
+        <string>Закрыть</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>