add tour place editor

This commit is contained in:
Chris Cromer 2019-01-20 03:01:24 -03:00
parent 84c10aae90
commit 1d32c70154
Signed by: cromer
GPG Key ID: 39CC813FF3C8708A
12 changed files with 1305 additions and 113 deletions

View File

@ -17,5 +17,6 @@
<file preprocess="xml-stripblanks">tour.list.ui</file>
<file preprocess="xml-stripblanks">tour.editor.ui</file>
<file preprocess="xml-stripblanks">tour.places.ui</file>
<file preprocess="xml-stripblanks">tour.place.editor.ui</file>
</gresource>
</gresources>

View File

@ -0,0 +1,555 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1
Copyright 2018-2019 Chris Cromer
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
<interface domain="sernatur">
<requires lib="gtk+" version="3.20"/>
<object class="GtkAdjustment" id="day_adjustment1">
<property name="lower">1</property>
<property name="upper">31</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="day_adjustment2">
<property name="lower">1</property>
<property name="upper">31</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="hour_adjustment1">
<property name="upper">23</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="hour_adjustment2">
<property name="upper">23</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="minute_adjustment1">
<property name="upper">59</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="minute_adjustment2">
<property name="upper">59</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="year_adjustment1">
<property name="lower">2010</property>
<property name="upper">2020</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkAdjustment" id="year_adjustment2">
<property name="lower">2010</property>
<property name="upper">2020</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<template class="SernaturTourPlaceEditor" parent="GtkApplicationWindow">
<property name="can_focus">False</property>
<property name="type">popup</property>
<property name="title" translatable="yes">Add Place</property>
<property name="resizable">False</property>
<property name="modal">True</property>
<property name="window_position">center-on-parent</property>
<property name="destroy_with_parent">True</property>
<property name="type_hint">dialog</property>
<property name="skip_taskbar_hint">True</property>
<property name="skip_pager_hint">True</property>
<property name="show_menubar">False</property>
<child>
<placeholder/>
</child>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="row_spacing">5</property>
<property name="column_spacing">5</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="label" translatable="yes">Place</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="label" translatable="yes">Arrival</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="label" translatable="yes">Departure</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkBox" id="button_bar">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="hexpand">True</property>
<property name="spacing">25</property>
<property name="homogeneous">True</property>
<child>
<object class="GtkButton" id="cancel">
<property name="label" translatable="yes">Cancel</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Cancel the modification of this place.</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<signal name="clicked" handler="on_clicked_button" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="save">
<property name="label" translatable="yes">Save</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Save this place.</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<signal name="clicked" handler="on_clicked_button" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">3</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="place">
<property name="visible">True</property>
<property name="can_focus">False</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="arrival">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<child>
<object class="GtkSpinButton" id="hour1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="caps_lock_warning">False</property>
<property name="input_purpose">number</property>
<property name="orientation">vertical</property>
<property name="adjustment">hour_adjustment1</property>
<property name="numeric">True</property>
<property name="value">12</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">:</property>
<attributes>
<attribute name="font-desc" value="Monospace Bold 18"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="minute1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="input_purpose">number</property>
<property name="orientation">vertical</property>
<property name="adjustment">minute_adjustment1</property>
<property name="numeric">True</property>
<property name="value">30</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="row_spacing">5</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="label" translatable="yes">Year</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="label" translatable="yes">Month</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="label" translatable="yes">Day</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="year1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="caps_lock_warning">False</property>
<property name="input_purpose">number</property>
<property name="adjustment">year_adjustment1</property>
<property name="numeric">True</property>
<property name="value">2010</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="day1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="caps_lock_warning">False</property>
<property name="input_purpose">number</property>
<property name="adjustment">day_adjustment1</property>
<property name="numeric">True</property>
<property name="value">1</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="month1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="active">0</property>
<items>
<item translatable="yes">January</item>
<item translatable="yes">February</item>
<item translatable="yes">March</item>
<item translatable="yes">April</item>
<item translatable="yes">May</item>
<item translatable="yes">June</item>
<item translatable="yes">July</item>
<item translatable="yes">August</item>
<item translatable="yes">September</item>
<item translatable="yes">October</item>
<item translatable="yes">November</item>
<item translatable="yes">December</item>
</items>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="departure">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<child>
<object class="GtkSpinButton" id="hour2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="text" translatable="yes">0</property>
<property name="caps_lock_warning">False</property>
<property name="input_purpose">number</property>
<property name="orientation">vertical</property>
<property name="adjustment">hour_adjustment2</property>
<property name="numeric">True</property>
<property name="value">12</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">:</property>
<attributes>
<attribute name="font-desc" value="Monospace Bold 18"/>
</attributes>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="minute2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="text" translatable="yes">0</property>
<property name="input_purpose">number</property>
<property name="orientation">vertical</property>
<property name="adjustment">minute_adjustment2</property>
<property name="numeric">True</property>
<property name="value">30</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="row_spacing">5</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="label" translatable="yes">Year</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="label" translatable="yes">Month</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="margin_left">5</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="label" translatable="yes">Day</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="year2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="halign">start</property>
<property name="caps_lock_warning">False</property>
<property name="input_purpose">number</property>
<property name="adjustment">year_adjustment2</property>
<property name="numeric">True</property>
<property name="value">2010</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkSpinButton" id="day2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="text" translatable="yes">1</property>
<property name="caps_lock_warning">False</property>
<property name="input_purpose">number</property>
<property name="adjustment">day_adjustment2</property>
<property name="numeric">True</property>
<property name="value">1</property>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
<child>
<object class="GtkComboBoxText" id="month2">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="active">0</property>
<items>
<item id="&lt;Enter ID&gt;" translatable="yes">January</item>
<item translatable="yes">February</item>
<item translatable="yes">March</item>
<item translatable="yes">April</item>
<item translatable="yes">May</item>
<item translatable="yes">June</item>
<item translatable="yes">July</item>
<item translatable="yes">August</item>
<item translatable="yes">September</item>
<item translatable="yes">October</item>
<item translatable="yes">November</item>
<item translatable="yes">December</item>
</items>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">1</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">3</property>
</packing>
</child>
</object>
<packing>
<property name="left_attach">1</property>
<property name="top_attach">2</property>
</packing>
</child>
</object>
</child>
</template>
</interface>

View File

@ -179,21 +179,6 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
<property name="hexpand">True</property>
<property name="column_spacing">25</property>
<property name="column_homogeneous">True</property>
<child>
<object class="GtkButton" id="edit_place">
<property name="label" translatable="yes">Edit</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Edit associated place.</property>
<signal name="clicked" handler="on_clicked_button" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="add_place">
<property name="label" translatable="yes">Add</property>
@ -208,21 +193,6 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="delete_place">
<property name="label" translatable="yes">Delete</property>
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="tooltip_text" translatable="yes">Delete associated place.</property>
<signal name="clicked" handler="on_clicked_button" swapped="no"/>
</object>
<packing>
<property name="left_attach">3</property>
<property name="top_attach">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="close_place">
<property name="label" translatable="yes">Close</property>
@ -233,7 +203,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
<signal name="clicked" handler="on_clicked_button" swapped="no"/>
</object>
<packing>
<property name="left_attach">4</property>
<property name="left_attach">2</property>
<property name="top_attach">0</property>
</packing>
</child>

View File

@ -34,8 +34,8 @@ namespace LibSernatur {
* @param hora_llegada The time of arrival
* @param fecha_salida The date of departure
* @param hora_salida The time of departure
* @param tour The tour taken
* @param lugar The place visisted
* @param tour The tour associated
* @param lugar The place associated
*/
public Asociado (string fecha_llegada = "", string hora_llegada = "", string fecha_salida = "", string hora_salida = "", Tour? tour = null, Lugar? lugar = null) {
this.fecha_llegada = fecha_llegada;
@ -175,6 +175,51 @@ namespace LibSernatur {
}
return list;
}
/**
* Insert an association in the database
* @param conn The database connection
* @param asociado The association to insert
* @throws PostgresError Thrown if there is a problem with escaping strings
* @throws DBError Thrown if an invalid value is passed
*/
public static void insert_asociado (Connection conn, Asociado asociado) throws PostgresError, DBError {
if (asociado.tour.id_tour == 0 || asociado.lugar.id_lugar == 0) {
throw new DBError.INVALID_VALUE (_ ("The id of the tour or the place is invalid!"));
}
var res = conn.db.exec (Query.get_query (conn, Query.Type.INSERT_ASSOCIATED, asociado));
if (res.get_status () != ExecStatus.COMMAND_OK) {
#if DEBUG
error (conn.db.get_error_message ());
#else
warning (conn.db.get_error_message ());
#endif
}
}
/**
* Delete an association in the database
* @param conn The database connection
* @param asociado The association to delete
* @throws PostgresError Thrown if there is a problem with escaping strings
* @throws DBError Thrown if an invalid value is passed
*/
public static void delete_associated (Connection conn, Asociado asociado) throws PostgresError, DBError {
if (asociado.tour.id_tour == 0 || asociado.lugar.id_lugar == 0) {
throw new DBError.INVALID_VALUE (_ ("The id of the tour or place is invalid!"));
}
var res = conn.db.exec (Query.get_query (conn, Query.Type.DELETE_TOUR, asociado));
if (res.get_status () != ExecStatus.COMMAND_OK) {
if (res.get_error_field (FieldCode.SQLSTATE) == "23503") {
throw new DBError.FOREIGN_KEY_CONSTAINT (res.get_error_field (FieldCode.MESSAGE_PRIMARY));
}
#if DEBUG
error (conn.db.get_error_message ());
#else
warning (conn.db.get_error_message ());
#endif
}
}
}
}
}

View File

@ -171,7 +171,7 @@ namespace LibSernatur {
/**
* Delete a tour in the database
* @param conn The database connection
* @param tour The tour to update
* @param tour The tour to delete
* @throws PostgresError Thrown if there is a problem with escaping strings
* @throws DBError Thrown if an invalid value is passed
*/

View File

@ -32,7 +32,9 @@ namespace LibSernatur {
INSERT_PLACE,
UPDATE_PLACE,
SELECT_ALL_ASSOCIATED,
SELECT_ALL_ASSOCIATED_BY_TOUR
SELECT_ALL_ASSOCIATED_BY_TOUR,
INSERT_ASSOCIATED,
DELETE_ASSOCIATED
}
public static string get_query (Connection conn, Type type, T? t) throws PostgresError {
@ -190,6 +192,30 @@ JOIN ciudad C2 ON (L.id_ciudad = C2.id_ciudad)
JOIN region R2 ON (C2.id_region = R2.id_region)
WHERE t.id_tour = " + tour.id_tour.to_string ();
case INSERT_ASSOCIATED:
Asociado asociado = (Asociado) t;
return "
INSERT INTO asociado
(id_tour, id_lugar, fecha_llegada, hora_llegada, fecha_salida, hora_salida)
VALUES
(
" + asociado.tour.id_tour.to_string () + ",
" + asociado.lugar.id_lugar.to_string () + ",
'" + conn.escape (asociado.fecha_llegada) + "',
'" + conn.escape (asociado.hora_llegada) + "',
'" + conn.escape (asociado.fecha_salida) + "',
'" + conn.escape (asociado.hora_salida) + "'
)";
case DELETE_ASSOCIATED:
Asociado asociado = (Asociado) t;
return "
DELETE FROM associated
WHERE (
id_tour = " + asociado.tour.id_tour.to_string () + " AND
id_lugar = " + asociado.lugar.id_lugar.to_string () + "
)";
default:
return "";
}

View File

@ -19,6 +19,7 @@ vala_sources = files(
'tour_list.vala',
'tour_editor.vala',
'tour_places.vala',
'tour_place_editor.vala',
'place_editor.vala',
'queries.vala',
'query_window.vala')

View File

@ -141,6 +141,9 @@ namespace Sernatur {
}
}
/**
* Reset the city dropdown
*/
private void reset_city () {
try {
cities = Ciudad.get_all_ciudades_in_region (conn, lugar.ciudad.region.id_region);

View File

@ -128,6 +128,10 @@ namespace Sernatur {
* The list that stores the cities for the combo box
*/
private Gtk.ListStore city_list_store = null;
/**
* A list of associated places, this is only used in the case of a new tour
*/
private List<Asociado> list_asociado = null;
/**
* This signal is called when a tour is saved
*/
@ -151,6 +155,9 @@ namespace Sernatur {
}
}
/**
* Reset the city dropdown
*/
private void reset_city () {
try {
cities = Ciudad.get_all_ciudades_in_region (conn, tour.ciudad.region.id_region);
@ -194,6 +201,152 @@ namespace Sernatur {
city_entry.set_text ("");
}
/**
* Validate the tour data before trying to insert it into the database
* @return Returns true if the data is valid
*/
private bool validate_tour_data () {
if (tour.nombre_tour.strip () == "") {
var msg = new Gtk.MessageDialog (this,
Gtk.DialogFlags.MODAL,
Gtk.MessageType.ERROR,
Gtk.ButtonsType.CLOSE,
_ ("Error: Tour name cannot be left blank!"));
msg.response.connect ((response_id) => {
msg.destroy ();
});
msg.set_title (_ ("Error"));
msg.run ();
return false;
}
bool list_success = true;
try {
List<Tour> list = Tour.get_all_tours (conn);
list.foreach ((entry) => {
if (tour.nombre_tour.down () == entry.nombre_tour.down ()) {
var msg = new Gtk.MessageDialog (this,
Gtk.DialogFlags.MODAL,
Gtk.MessageType.ERROR,
Gtk.ButtonsType.CLOSE,
_ ("Error: A tour named \"%s\" already exists!"), entry.nombre_tour);
msg.response.connect ((response_id) => {
msg.destroy ();
});
msg.set_title (_ ("Error"));
msg.run ();
list_success = false;
}
});
}
catch (Error e) {
#if DEBUG
error (e.message);
#else
warning (e.message);
return false;
#endif
}
return list_success;
}
/**
* Validate the city data before trying to insert it into the database
* @param ciudad The city to validate
* @return Returns true if the data is valid
*/
private bool validate_city_data (Ciudad ciudad) {
if (ciudad.nombre_ciudad.strip () == "") {
var msg = new Gtk.MessageDialog (this,
Gtk.DialogFlags.MODAL,
Gtk.MessageType.ERROR,
Gtk.ButtonsType.CLOSE,
_ ("Error: City name cannot be left blank!"));
msg.response.connect ((response_id) => {
msg.destroy ();
});
msg.set_title (_ ("Error"));
msg.run ();
return false;
}
bool list_success = true;
try {
List<Ciudad> list = Ciudad.get_all_ciudades (conn);
list.foreach ((entry) => {
if (ciudad.nombre_ciudad.down () == entry.nombre_ciudad.down ()) {
var msg = new Gtk.MessageDialog (this,
Gtk.DialogFlags.MODAL,
Gtk.MessageType.ERROR,
Gtk.ButtonsType.CLOSE,
_ ("Error: A city named \"%s\" already exists!"), entry.nombre_ciudad);
msg.response.connect ((response_id) => {
msg.destroy ();
});
msg.set_title (_ ("Error"));
msg.run ();
list_success = false;
}
});
}
catch (Error e) {
#if DEBUG
error (e.message);
#else
warning (e.message);
return false;
#endif
}
return list_success;
}
/**
* Validate the region data before trying to insert it into the database
* @param region The region to validate
* @return Returns true if the data is valid
*/
private bool validate_region_data (Region region) {
if (region.nombre_region.strip () == "") {
var msg = new Gtk.MessageDialog (this,
Gtk.DialogFlags.MODAL,
Gtk.MessageType.ERROR,
Gtk.ButtonsType.CLOSE,
_ ("Error: Region name cannot be left blank!"));
msg.response.connect ((response_id) => {
msg.destroy ();
});
msg.set_title (_ ("Error"));
msg.run ();
return false;
}
bool list_success = true;
try {
List<Region> list = Region.get_all_regiones (conn);
list.foreach ((entry) => {
if (region.nombre_region.down () == entry.nombre_region.down ()) {
var msg = new Gtk.MessageDialog (this,
Gtk.DialogFlags.MODAL,
Gtk.MessageType.ERROR,
Gtk.ButtonsType.CLOSE,
_ ("Error: A region named \"%s\" already exists!"), entry.nombre_region);
msg.response.connect ((response_id) => {
msg.destroy ();
});
msg.set_title (_ ("Error"));
msg.run ();
list_success = false;
}
});
}
catch (Error e) {
#if DEBUG
error (e.message);
#else
warning (e.message);
return false;
#endif
}
return list_success;
}
/**
* This callback is called when a button is clicked
* @param button The button that was clicked
@ -201,38 +354,51 @@ namespace Sernatur {
[GtkCallback]
public void on_clicked_button (Gtk.Button button) {
if (button == save) {
if (tour.id_tour == 0) {
update_tour_instance ();
try {
Tour.insert_tour (conn, tour);
if (update_tour_instance () && validate_tour_data ()) {
if (tour.id_tour == 0) {
try {
Tour.insert_tour (conn, tour);
if (list_asociado != null) {
// Insert all the pending associations
list_asociado.foreach ((entry) => {
try {
Asociado.insert_asociado (conn, entry);
}
catch (Error e) {
#if DEBUG
error (e.message);
#else
warning (e.message);
#endif
}
});
}
save_tour (); // Signal the parent to update itself
this.close ();
}
catch (Error e) {
#if DEBUG
error (e.message);
#else
warning (e.message);
#endif
}
}
catch (Error e) {
#if DEBUG
error (e.message);
#else
warning (e.message);
#endif
}
finally {
save_tour (); // Signal the parent to update itself
this.close ();
}
}
else {
update_tour_instance ();
try {
Tour.update_tour (conn, tour);
}
catch (Error e) {
#if DEBUG
error (e.message);
#else
warning (e.message);
#endif
}
finally {
save_tour (); // Signal the parent to update itself
this.close ();
else {
try {
Tour.update_tour (conn, tour);
}
catch (Error e) {
#if DEBUG
error (e.message);
#else
warning (e.message);
#endif
}
finally {
save_tour (); // Signal the parent to update itself
this.close ();
}
}
}
}
@ -241,24 +407,38 @@ namespace Sernatur {
tour_places.set_transient_for (this); // Set this window as the parent of the new window
tour_places.initialize ();
tour_places.show_all ();
tour_places.save_places.connect (on_save_places);
}
else if (button == cancel) {
this.close ();
}
}
/**
* Called when some associated places are saved and a tour id doesn't exist yet
* @param tour_places The TourPlaces instance that called this signal
* @param list_asociado The list of new associations
*/
private void on_save_places (TourPlaces tour_places, List<Asociado> list_asociado) {
this.list_asociado = list_asociado.copy ();
places.sensitive = false;
}
/**
* Update the the tour object with new info from the editor
*/
private void update_tour_instance () {
tour.nombre_tour = tour_name.get_text ();
private bool update_tour_instance () {
tour.nombre_tour = tour_name.get_text ().strip ();
tour.costo_indiv = (uint) int.parse (indiv_cost.get_text ());
tour.costo_grupal = (uint) int.parse (group_cost.get_text ());
tour.minima_personas = (uint) minimum_people.get_value_as_int ();
if (region.get_active () == -1) {
Region new_region = new Region (0, region.get_active_text ());
Region new_region = new Region (0, region.get_active_text ().strip ());
try {
new_region.id_region = Region.insert_region (conn, new_region);
if (validate_region_data (new_region)) {
new_region.id_region = Region.insert_region (conn, new_region);
update_tour_instance_city (new_region);
}
}
catch (Error e) {
#if DEBUG
@ -267,9 +447,6 @@ namespace Sernatur {
warning (e.message);
#endif
}
finally {
update_tour_instance_city (new_region);
}
}
else {
Region new_region;
@ -284,19 +461,22 @@ namespace Sernatur {
}
update_tour_instance_city (new_region);
}
return true;
}
/**
* This method updates the city part of the tour instance
* @param new_region The region to insert into the city object
*/
private void update_tour_instance_city (Region new_region) {
private bool update_tour_instance_city (Region new_region) {
Ciudad ciudad;
if (city.get_active () == -1) {
ciudad = new Ciudad (0, city.get_active_text ());
ciudad = new Ciudad (0, city.get_active_text ().strip ());
ciudad.region = new_region;
try {
ciudad.id_ciudad = Ciudad.insert_city (conn, ciudad);
if (validate_city_data (ciudad)) {
ciudad.id_ciudad = Ciudad.insert_city (conn, ciudad);
}
}
catch (Error e) {
#if DEBUG
@ -319,6 +499,7 @@ namespace Sernatur {
tour.ciudad = ciudad;
}
}
return true;
}
/**

View File

@ -253,13 +253,7 @@ namespace Sernatur {
}
msg.destroy ();
});
msg.set_title (_ ("Error"));
msg.show ();
selection.selected_foreach ((model, path, iter) => {
Tour tour;
model.get (iter,
Column.TOUR, out tour);
});
}
else if (button == close_tour) {
this.close ();

283
src/tour_place_editor.vala Normal file
View File

@ -0,0 +1,283 @@
/*
* Copyright 2018-2019 Chris Cromer
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
namespace Sernatur {
using LibSernatur.DB;
/**
* The tour add place window class
*/
[GtkTemplate (ui = "/cl/cromer/ubb/sernatur/tour.place.editor.ui")]
public class TourPlaceEditor : Gtk.ApplicationWindow {
/**
* The open database connection
*/
private Connection conn;
/**
* The place to edit
*/
private Asociado asociado;
/**
* The place data stored in the list store
*/
private enum Column {
/**
* The place name
*/
PLACE_NAME,
/**
* The place object
*/
PLACE,
/**
* The number of colums in this enum
*/
N_COLUMNS
}
/**
* The save button
*/
[GtkChild]
private Gtk.Button save;
/**
* The cancel button
*/
[GtkChild]
private Gtk.Button cancel;
/**
* The place
*/
[GtkChild]
private Gtk.ComboBoxText place;
/**
* The arrival year
*/
[GtkChild]
private Gtk.SpinButton year1;
/**
* The arrival month
*/
[GtkChild]
private Gtk.ComboBoxText month1;
/**
* The arrival day
*/
[GtkChild]
private Gtk.SpinButton day1;
/**
* The departure year
*/
[GtkChild]
private Gtk.SpinButton year2;
/**
* The departure month
*/
[GtkChild]
private Gtk.ComboBoxText month2;
/**
* The departure day
*/
[GtkChild]
private Gtk.SpinButton day2;
/**
* The arrival hour
*/
[GtkChild]
private Gtk.SpinButton hour1;
/**
* The arrival minute
*/
[GtkChild]
private Gtk.SpinButton minute1;
/**
* The departure hour
*/
[GtkChild]
private Gtk.SpinButton hour2;
/**
* The departure minute
*/
[GtkChild]
private Gtk.SpinButton minute2;
/**
* A list of the places from the database
*/
private List<Lugar> places;
/**
* The list that stores the places for the combo box
*/
private Gtk.ListStore places_list_store;
/**
* This signal is called when a place is saved
*/
public signal void save_place (Asociado asociado);
/**
* This callback is called when a button is clicked
* @param button The button that was clicked
*/
[GtkCallback]
public void on_clicked_button (Gtk.Button button) {
if (button == save) {
if (asociado.tour.id_tour == 0) {
update_place_instance ();
if (asociado.lugar.id_lugar != 0) {
save_place (asociado); // Signal the parent to update itself
this.close ();
}
}
else if (asociado.fecha_llegada == "") {
update_place_instance ();
try {
Asociado.insert_asociado (conn, asociado);
}
catch (Error e) {
#if DEBUG
error (e.message);
#else
warning (e.message);
#endif
}
finally {
save_place (asociado); // Signal the parent to update itself
this.close ();
}
}
else {
// Not implemented due to lack of time
/*update_place_instance ();
try {
Asociado.update_asociado (conn, lugar);
}
catch (Error e) {
#if DEBUG
error (e.message);
#else
warning (e.message);
#endif
}
finally {
save_place (asociado); // Signal the parent to update itself
this.close ();
}*/
}
}
else if (button == cancel) {
this.close ();
}
}
/**
* Update the the place object with new info from the editor
*/
private void update_place_instance () {
if (place.get_active () > 0) {
Lugar lugar;
Gtk.TreeIter iter;
place.get_active_iter (out iter);
if (places_list_store.iter_is_valid (iter)) {
places_list_store.get (iter,
Column.PLACE, out lugar);
asociado.lugar = lugar;
}
}
asociado.fecha_llegada = year1.get_value ().to_string () + "-" +
(month1.get_active () + 1).to_string () + "-" +
day1.get_value ().to_string ();
asociado.hora_llegada = hour1.get_value ().to_string () + ":" +
minute1.get_value ().to_string () + ":00";
asociado.fecha_salida = year2.get_value ().to_string () + "-" +
(month2.get_active () + 1).to_string () + "-" +
day2.get_value ().to_string ();
asociado.hora_salida = hour2.get_value ().to_string () + ":" +
minute2.get_value ().to_string () + ":00";
}
/**
* Initialize the tour place editor class
* @param application The application used to make the GLib object
* @param conn The database connection to use
* @param asociado The place to edit
*/
public TourPlaceEditor (Gtk.Application application, Connection conn, Asociado asociado) {
Object (application: application);
this.conn = conn;
this.asociado = asociado;
}
/**
* Initialize what is needed for this window
*/
public void initialize () {
try {
places = Lugar.get_all_lugares (conn);
}
catch (Error e) {
#if DEBUG
error (e.message);
#else
warning (e.message);
#endif
}
places.sort_with_data ((a, b) => {
return strcmp (a.nombre_lugar, b.nombre_lugar);
});
places_list_store = new Gtk.ListStore (Column.N_COLUMNS,
typeof (string),
typeof (Lugar));
place.set_model (places_list_store);
if (asociado.fecha_llegada != "") {
string[] arrival_date = asociado.fecha_llegada.split ("-", 3);
year1.set_value (double.parse (arrival_date[0]));
month1.set_active (int.parse (arrival_date[1]) - 1);
day1.set_value (double.parse (arrival_date[2]));
string[] departure_date = asociado.fecha_salida.split ("-", 3);
year2.set_value (double.parse (departure_date[0]));
month2.set_active (int.parse (departure_date[1]) - 1);
day2.set_value (double.parse (departure_date[2]));
string[] arrival_time = asociado.hora_llegada.split (":", 2);
hour1.set_value (double.parse (arrival_time[0]));
minute1.set_value (double.parse (arrival_time[1]));
string[] departure_time = asociado.hora_salida.split (":", 2);
hour2.set_value (double.parse (departure_time[0]));
minute2.set_value (double.parse (departure_time[1]));
}
else {
asociado.lugar = new Lugar ();
}
places.foreach ((entry) => {
Gtk.TreeIter iter;
places_list_store.append (out iter);
places_list_store.set (iter,
Column.PLACE_NAME, entry.nombre_lugar,
Column.PLACE, entry);
if (entry.id_lugar == asociado.lugar.id_lugar) {
place.set_active_iter (iter);
}
});
}
}
}

View File

@ -75,7 +75,7 @@ namespace Sernatur {
*/
private Gtk.ListStore list_store;
/**
* The list of tours
* The list of associations
*/
private List<Asociado> associated_list;
/**
@ -83,11 +83,6 @@ namespace Sernatur {
*/
[GtkChild]
private Gtk.TreeView place_tree;
/**
* The row selection
*/
[GtkChild]
private Gtk.TreeSelection selection;
/**
* The place name column
*/
@ -128,16 +123,6 @@ namespace Sernatur {
*/
[GtkChild]
private Gtk.Button new_place;
/**
* The edit place button
*/
[GtkChild]
private Gtk.Button edit_place;
/**
* The delete place button
*/
[GtkChild]
private Gtk.Button delete_place;
/**
* The close place button
*/
@ -148,6 +133,14 @@ namespace Sernatur {
*/
[GtkChild]
private Gtk.Button add_place;
/**
* The associated tours to save if the user decides to save the tour
*/
private List<Asociado> asociado_list = new List<Asociado> ();
/**
* This signal is called when there are places that need to be saved after the tour is created
*/
public signal void save_places (List<Asociado> asociado_list);
/**
* This callback is called when the user clicks on a row
@ -155,7 +148,8 @@ namespace Sernatur {
*/
[GtkCallback]
private void on_changed_selection(Gtk.TreeSelection selection) {
if (selection.count_selected_rows () == 1) {
// Not implemented due to lack of time
/*if (selection.count_selected_rows () == 1) {
edit_place.sensitive = true;
delete_place.sensitive =true;
}
@ -166,7 +160,7 @@ namespace Sernatur {
else {
edit_place.sensitive = false;
delete_place.sensitive = false;
}
}*/
}
/**
@ -180,18 +174,110 @@ namespace Sernatur {
place_editor.set_transient_for (this); // Set this window as the parent of the new window
place_editor.initialize ();
place_editor.show_all ();
place_editor.save_place.connect (on_save);
place_editor.save_place.connect (on_save_place);
}
else if (button == add_place) {
var place_editor = new TourPlaceEditor (application, conn, new Asociado ("", "", "", "", tour, null));
place_editor.set_transient_for (this); // Set this window as the parent of the new window
place_editor.initialize ();
place_editor.show_all ();
place_editor.save_place.connect (on_save_tour_place);
}
else if (button == edit_place) {
/*else if (button == edit_place) {
Gtk.TreeModel model;
var path = selection.get_selected_rows (out model);
path.foreach ((entry) => {
var tree_row_reference = new Gtk.TreeRowReference (model, entry);
Gtk.TreeIter iter;
list_store.get_iter (out iter, tree_row_reference.get_path ());
Asociado asociado;
model.get (iter,
Column.ASSOCIATED, out asociado);
var place_editor = new TourPlaceEditor (application, conn, asociado);
place_editor.set_transient_for (this); // Set this window as the parent of the new window
place_editor.initialize ();
place_editor.show_all ();
place_editor.save_place.connect (on_save_tour_place);
});
}
else if (button == delete_place) {
Gtk.MessageDialog msg;
if (selection.count_selected_rows () == 1) {
msg = new Gtk.MessageDialog (this,
Gtk.DialogFlags.MODAL,
Gtk.MessageType.ERROR,
Gtk.ButtonsType.YES_NO,
_ ("Are you sure you wish to delete this associated place?"));
}
else {
msg = new Gtk.MessageDialog (this,
Gtk.DialogFlags.MODAL,
Gtk.MessageType.ERROR,
Gtk.ButtonsType.YES_NO,
_ ("Are you sure you wish to delete these associated places?"));
}
msg.response.connect ((response_id) => {
switch (response_id) {
case Gtk.ResponseType.YES:
Gtk.TreeModel model;
var path = selection.get_selected_rows (out model);
path.foreach ((entry) => {
var tree_row_reference = new Gtk.TreeRowReference (model, entry);
Gtk.TreeIter iter;
list_store.get_iter (out iter, tree_row_reference.get_path ());
Asociado asociado;
model.get (iter,
Column.ASSOCIATED, out asociado);
try {
Asociado.delete_associated (conn, asociado);
}
catch (PostgresError e) {
#if DEBUG
error (e.message);
#else
warning (e.message);
#endif
}
catch (DBError e) {
if (e.code == 1) {
warning (e.message);
var msg2 = new Gtk.MessageDialog (this,
Gtk.DialogFlags.MODAL,
Gtk.MessageType.ERROR,
Gtk.ButtonsType.CLOSE,
_ ("Error: Could not delete the associated place \"%s\"!"), asociado.lugar.nombre_lugar);
msg2.response.connect ((response_id) => {
msg2.destroy ();
});
msg2.set_title (_ ("Error"));
msg2.run ();
}
else {
#if DEBUG
error (e.message);
#else
warning (e.message);
#endif
}
}
});
edit_place.sensitive = false;
delete_place.sensitive = false;
reset_columns ();
}
list_store.clear ();
update_list_store ();
break;
}
msg.destroy ();
});
msg.show ();
}*/
else if (button == close_place) {
if (asociado_list.length () > 0) {
save_places (asociado_list);
}
this.close ();
}
}
@ -202,8 +288,8 @@ namespace Sernatur {
*/
[GtkCallback]
private void on_clicked_column (Gtk.TreeViewColumn column) {
edit_place.sensitive = false;
delete_place.sensitive = false;
/*edit_place.sensitive = false;
delete_place.sensitive = false;*/
if (column == place_name) {
if (!place_name.sort_indicator) {
reset_columns ();
@ -405,6 +491,19 @@ namespace Sernatur {
Column.DEPARTURE_TIME, entry.hora_salida,
Column.ASSOCIATED, entry);
});
asociado_list.foreach ((entry) => {
Gtk.TreeIter iter;
list_store.append (out iter);
list_store.set (iter,
Column.PLACE_NAME, entry.lugar.nombre_lugar,
Column.TICKET_PRICE, Money.format_uint (entry.lugar.valor_entrada),
Column.DIFFICULTY, entry.lugar.nivel,
Column.ARRIVAL_DATE, entry.fecha_llegada,
Column.ARRIVAL_TIME, entry.hora_llegada,
Column.DEPARTURE_DATE, entry.fecha_salida,
Column.DEPARTURE_TIME, entry.hora_salida,
Column.ASSOCIATED, entry);
});
}
/**
@ -431,14 +530,46 @@ namespace Sernatur {
* Called when a new or old place is saved
* @param place_editor The editor that saved the place
*/
public void on_save(PlaceEditor place_editor) {
edit_place.sensitive = false;
delete_place.sensitive = false;
public void on_save_place(PlaceEditor place_editor) {
// Not implemented due to lack of time
/*edit_place.sensitive = false;
delete_place.sensitive = false;*/
reset_columns ();
list_store.clear ();
if (tour.id_tour != 0) {
list_store.clear ();
update_list_store ();
}
}
update_list_store ();
/**
* Called when a new or old place is saved
* @param place_editor The editor that saved the place
*/
public void on_save_tour_place(TourPlaceEditor place_editor, Asociado asociado) {
// Not implemented due to lack of time
/*edit_place.sensitive = false;
delete_place.sensitive = false;*/
reset_columns ();
if (tour.id_tour == 0) {
asociado_list.append (asociado);
Gtk.TreeIter iter;
list_store.append (out iter);
list_store.set (iter,
Column.PLACE_NAME, asociado.lugar.nombre_lugar,
Column.TICKET_PRICE, Money.format_uint (asociado.lugar.valor_entrada),
Column.DIFFICULTY, asociado.lugar.nivel,
Column.ARRIVAL_DATE, asociado.fecha_llegada,
Column.ARRIVAL_TIME, asociado.hora_llegada,
Column.DEPARTURE_DATE, asociado.fecha_salida,
Column.DEPARTURE_TIME, asociado.hora_salida,
Column.ASSOCIATED, asociado);
}
else {
list_store.clear ();
update_list_store ();
}
}
/**
@ -499,7 +630,9 @@ namespace Sernatur {
typeof (string),
typeof (Asociado));
update_list_store ();
if (tour.id_tour != 0) {
update_list_store ();
}
place_tree.set_model (list_store);
}