3
* Copyright (C) 2001-2004 Murray Cumming
5
* This program is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU General Public License as
7
* published by the Free Software Foundation; either version 2 of the
8
* License, or (at your option) any later version.
10
* This program is distributed in the hope that it will be useful, but
11
* WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
* General Public License for more details.
15
* You should have received a copy of the GNU General Public
16
* License along with this program; if not, write to the
17
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18
* Boston, MA 02111-1307, USA.
22
#include "dialog_relationships_overview.h"
23
#include "glom/utility_widgets/canvas/canvas_line_movable.h"
24
#include "glom/utility_widgets/canvas/canvas_text_movable.h"
25
#include <glom/mode_design/layout/dialog_choose_relationship.h>
26
#include "printoperation_relationshipsoverview.h"
27
#include "glom/application.h"
28
#include <goocanvas.h>
29
#include <glibmm/i18n.h>
36
int Dialog_RelationshipsOverview::m_last_size_x = 0;
37
int Dialog_RelationshipsOverview::m_last_size_y = 0;
40
Dialog_RelationshipsOverview::Dialog_RelationshipsOverview(BaseObjectType* cobject, const Glib::RefPtr<Gtk::Builder>& builder)
41
: Gtk::Dialog(cobject),
44
m_scrolledwindow_canvas(0)
46
m_refPageSetup = Gtk::PageSetup::create();
47
m_refSettings = Gtk::PrintSettings::create();
51
builder->get_widget("vbox_placeholder_menubar", vbox);
53
m_refActionGroup = Gtk::ActionGroup::create();
55
m_refActionGroup->add(Gtk::Action::create("Overview_MainMenu_File", _("_File")) );
56
m_refActionGroup->add(Gtk::Action::create("Overview_MainMenu_File_PageSetup", _("Page _Setup")),
57
sigc::mem_fun(*this, &Dialog_RelationshipsOverview::on_menu_file_page_setup) );
58
m_refActionGroup->add(Gtk::Action::create("Overview_MainMenu_File_Print", Gtk::Stock::PRINT),
59
sigc::mem_fun(*this, &Dialog_RelationshipsOverview::on_menu_file_print) );
61
m_refActionGroup->add(Gtk::Action::create("Overview_MainMenu_View", _("_View")) );
62
m_action_showgrid = Gtk::ToggleAction::create("Overview_MainMenu_View_Grid", _("Show _Grid"));
63
m_refActionGroup->add(m_action_showgrid,
64
sigc::mem_fun(*this, &Dialog_RelationshipsOverview::on_menu_view_showgrid) );
66
Glib::RefPtr<Gtk::UIManager> m_refUIManager = Gtk::UIManager::create();
68
m_refUIManager->insert_action_group(m_refActionGroup);
70
#ifdef GLIBMM_EXCEPTIONS_ENABLED
74
static const Glib::ustring ui_description =
76
#ifdef GLOM_ENABLE_MAEMO
77
" <popup name='Overview_MainMenu'>"
79
" <menubar name='Overview_MainMenu'>"
81
" <menu action='Overview_MainMenu_File'>"
82
" <menuitem action='Overview_MainMenu_File_PageSetup' />"
83
" <menuitem action='Overview_MainMenu_File_Print' />"
85
" <menu action='Overview_MainMenu_View'>"
86
" <menuitem action='Overview_MainMenu_View_Grid' />"
88
#ifdef GLOM_ENABLE_MAEMO
95
#ifdef GLIBMM_EXCEPTIONS_ENABLED
96
m_refUIManager->add_ui_from_string(ui_description);
98
catch(const Glib::Error& ex)
100
std::cerr << "building menus failed: " << ex.what();
103
std::auto_ptr<Glib::Error> error;
104
m_refUIManager->add_ui_from_string(ui_info, error);
105
if(error.get() != NULL)
107
std::cerr << "building menus failed: " << error->what();
112
m_menu = dynamic_cast<Gtk::MenuBar*>( m_refUIManager->get_widget("/Overview_MainMenu") );
114
g_warning("menu not found");
116
vbox->pack_start(*m_menu, Gtk::PACK_SHRINK);
120
//Get the scolled window and add the canvas to it:
121
m_scrolledwindow_canvas = 0;
122
builder->get_widget("scrolledwindow_canvas", m_scrolledwindow_canvas);
124
m_scrolledwindow_canvas->add(m_canvas);
127
//Restore the previous window size, to avoid annoying the user:
128
if(m_last_size_x != 0 && m_last_size_y != 0 )
130
set_default_size(m_last_size_x, m_last_size_y);
133
m_group_tables = Goocanvas::Group::create();
134
m_canvas.add_item(m_group_tables);
135
m_group_lines = Goocanvas::Group::create();
136
m_canvas.add_item(m_group_lines);
137
m_group_lines->lower(); //Make sure that the lines are below the tables.
139
//Respond to changes of window size,
140
//so we always make the canvas bounds big enough:
141
m_scrolledwindow_canvas->get_hadjustment()->signal_changed().connect(
142
sigc::mem_fun(*this, &Dialog_RelationshipsOverview::on_scroll_value_changed) );
143
m_scrolledwindow_canvas->get_vadjustment()->signal_changed().connect(
144
sigc::mem_fun(*this, &Dialog_RelationshipsOverview::on_scroll_value_changed) );
146
setup_context_menu();
149
Dialog_RelationshipsOverview::~Dialog_RelationshipsOverview()
151
get_size(m_last_size_x, m_last_size_y);
155
void Dialog_RelationshipsOverview::draw_tables()
157
//Remove all current items:
158
while(m_group_tables->get_n_children() > 0)
159
m_group_tables->remove_child(0);
161
Document* document = dynamic_cast<Document*>(get_document());
164
double max_table_height = 0;
168
//Create tables canvas items, with lists of fields:
169
Document::type_listTableInfo tables = document->get_tables();
170
for(Document::type_listTableInfo::iterator iter = tables.begin(); iter != tables.end(); ++iter)
172
sharedptr<TableInfo> info = *iter;
173
const Glib::ustring table_name = info->get_name();
177
//Get the x and y position from the document:
178
if(!document->get_table_overview_position(table_name, table_x, table_y))
182
document->set_table_overview_position(table_name, table_x, table_y);
186
Document::type_vec_fields fields = document->get_table_fields(table_name);
188
Glib::RefPtr<CanvasGroupDbTable> table_group =
189
CanvasGroupDbTable::create(info->get_name(), info->get_title_or_name(), fields, table_x, table_y);
190
m_group_tables->add_child(table_group);
192
table_group->signal_moved().connect( sigc::bind(
193
sigc::mem_fun(*this, &Dialog_RelationshipsOverview::on_table_moved),
195
table_group->signal_show_context().connect( sigc::bind(
196
sigc::mem_fun(*this, &Dialog_RelationshipsOverview::on_table_show_context),
199
//tv->x2 = tv->x1 + table_width;
200
//tv->y2 = tv->y1 + table_height;
202
sizex += table_group->get_table_width() + 10;
204
max_table_height = std::max(max_table_height, table_group->get_table_height());
207
m_canvas.set_bounds(0, 0, sizex, max_table_height * tables.size());
211
void Dialog_RelationshipsOverview::draw_lines()
213
//Remove all current items:
214
while(m_group_lines->get_n_children() > 0)
215
m_group_lines->remove_child(0);
217
Document* document = dynamic_cast<Document*>(get_document());
220
//Create the lines linking tables to show relationships:
221
Document::type_listTableInfo tables = document->get_tables();
222
for(Document::type_listTableInfo::iterator iter = tables.begin(); iter != tables.end(); ++iter)
224
sharedptr<TableInfo> info = *iter;
225
const Glib::ustring table_name = info->get_name();
227
Document::type_vec_relationships m_relationships = document->get_relationships(table_name);
228
Document::type_vec_fields fields = document->get_table_fields(table_name);
230
for(Document::type_vec_relationships::const_iterator rit = m_relationships.begin(); rit != m_relationships.end(); rit++)
232
sharedptr<const Relationship> relationship = *rit;
236
Glib::RefPtr<CanvasGroupDbTable> group_from = get_table_group(relationship->get_from_table());
238
double from_field_x = 0.0;
239
double from_field_y = 0.0;
245
group_from->get_xy(temp_x, temp_y);
247
from_field_x = temp_x;
248
from_field_y = temp_y + group_from->get_field_y(relationship->get_from_field());
251
//Only primary keys can be to fields:
252
if(true) //document->get_field(relationship->get_to_table(), relationship->get_to_field())->get_primary_key())
254
Glib::RefPtr<CanvasGroupDbTable> group_to = get_table_group(relationship->get_to_table());
256
double to_field_x = 0.0;
257
double to_field_y = 0.0;
263
group_to->get_xy(temp_x, temp_y);
265
to_field_y = temp_y + group_to->get_field_y(relationship->get_to_field());
268
//Start the line from the right of the from table instead of the left, if the to table is to the right:
269
double extra_line = 0; //An extra horizontal line before the real diagonal line starts.
270
if(to_field_x > from_field_x)
272
from_field_x += group_from->get_table_width();
277
to_field_x += group_to->get_table_width();
282
Glib::RefPtr<CanvasLineMovable> line = CanvasLineMovable::create();
283
double points_coordinates[] = {from_field_x, from_field_y,
284
from_field_x + extra_line, from_field_y,
285
to_field_x - extra_line, to_field_y,
286
to_field_x, to_field_y};
287
Goocanvas::Points points(4, points_coordinates);
288
line->property_points() = points;
289
line->property_stroke_color() = "black";
290
line->property_line_width() = 1.0;
291
line->property_start_arrow() = false;
292
line->property_end_arrow() = true;
293
line->property_arrow_width() = 10.0;
294
line->property_arrow_length() = 10.0;
295
line->set_movement_allowed(false, false); //Don't let the user move this by dragging.
296
m_group_lines->add_child(line);
298
//Create a text item, showing the name of the relationship on the line:
300
//Raise or lower the text slightly to make it show above the line when horizontal,
301
//and to avoid overwriting a relationship in the other direction:
302
//TODO: This is not very clear. Investigate how other systems show this.
303
double y_offset = (from_field_x < to_field_x) ? -10 : +10;
304
if(from_field_x == to_field_x)
305
y_offset = (from_field_y < to_field_y) ? -10 : +10;
307
const double text_x = (from_field_x + to_field_x) / 2;
308
const double text_y = ((from_field_y + to_field_y) / 2) + y_offset;
309
Glib::RefPtr<CanvasTextMovable> text = CanvasTextMovable::create(relationship->get_title_or_name(),
310
text_x, text_y, -1, //TODO: Calc a suitable width.
312
text->property_font() = "sans 10";
313
text->property_use_markup() = true;
314
text->set_movement_allowed(false, false); //Move only as part of the parent group.
315
m_group_lines->add_child(text);
322
std::cout << "ERROR: Could not retrieve the Glom document." << std::endl;
326
void Dialog_RelationshipsOverview::load_from_document()
332
void Dialog_RelationshipsOverview::on_response(int /* id */)
334
if(m_modified && get_document())
335
get_document()->set_modified();
340
void Dialog_RelationshipsOverview::on_menu_file_print()
342
print_or_preview(Gtk::PRINT_OPERATION_ACTION_PRINT_DIALOG);
345
void Dialog_RelationshipsOverview::on_menu_file_page_setup()
347
//Show the page setup dialog, asking it to start with the existing settings:
348
Glib::RefPtr<Gtk::PageSetup> new_page_setup =
349
Gtk::run_page_setup_dialog(*this, m_refPageSetup, m_refSettings);
351
//Save the chosen page setup dialog for use when printing, previewing, or
352
//showing the page setup dialog again:
353
m_refPageSetup = new_page_setup;
356
void Dialog_RelationshipsOverview::on_menu_view_showgrid()
358
if(m_action_showgrid->get_active())
360
std::cout << "showing" << std::endl;
361
m_canvas.set_grid_gap(40);
365
std::cout << "hiding" << std::endl;
366
m_canvas.remove_grid();
370
void Dialog_RelationshipsOverview::on_menu_file_save()
374
void Dialog_RelationshipsOverview::print_or_preview(Gtk::PrintOperationAction print_action)
376
//Create a new PrintOperation with our PageSetup and PrintSettings:
377
//(We use our derived PrintOperation class)
378
Glib::RefPtr<PrintOperationRelationshipsOverview> print = PrintOperationRelationshipsOverview::create();
379
print->set_canvas(&m_canvas);
381
print->set_track_print_status();
382
print->set_default_page_setup(m_refPageSetup);
383
print->set_print_settings(m_refSettings);
385
//print->signal_done().connect(sigc::bind(sigc::mem_fun(*this,
386
// &ExampleWindow::on_printoperation_done), print));
390
print->run(print_action /* print or preview */, *this);
392
catch (const Gtk::PrintError& ex)
394
//See documentation for exact Gtk::PrintError error codes.
395
std::cerr << "An error occurred while trying to run a print operation:"
396
<< ex.what() << std::endl;
400
Glib::RefPtr<CanvasGroupDbTable> Dialog_RelationshipsOverview::get_table_group(const Glib::ustring& table_name)
402
const int count = m_group_tables->get_n_children();
403
for(int i = 0; i < count; ++i)
405
Glib::RefPtr<Goocanvas::Item> item = m_group_tables->get_child(i);
406
Glib::RefPtr<CanvasGroupDbTable> table_item = Glib::RefPtr<CanvasGroupDbTable>::cast_dynamic(item);
407
if(table_item && (table_item->get_table_name() == table_name))
414
return Glib::RefPtr<CanvasGroupDbTable>();
417
void Dialog_RelationshipsOverview::on_table_moved(const Glib::RefPtr<CanvasGroupDbTable>& table)
419
Document* document = dynamic_cast<Document*>(get_document());
420
if(document && table)
422
//Save the new position in the document:
426
document->set_table_overview_position(table->get_table_name(), x, y);
429
//It is probably incredibly inefficient to recreate the lines repeatedly while dragging a table,
430
//but it seems to work OK, and it makes the code much simpler.
431
//If this is a problem, we should just change the start/end coordinates of any lines connected to the moved table.
435
void Dialog_RelationshipsOverview::on_table_show_context(guint button, guint32 activate_time, const Glib::RefPtr<CanvasGroupDbTable>& table)
437
if(m_action_edit_fields)
439
// Disconnect the previous handler,
440
// and connect a new one, with the correct table as a bound parameter:
441
m_connection_edit_fields.disconnect();
442
m_connection_edit_fields = m_action_edit_fields->signal_activate().connect(
443
sigc::bind( sigc::mem_fun(*this, &Dialog_RelationshipsOverview::on_context_menu_edit_fields), table ));
445
m_connection_edit_relationships.disconnect();
446
m_connection_edit_relationships = m_action_edit_relationships->signal_activate().connect(
447
sigc::bind( sigc::mem_fun(*this, &Dialog_RelationshipsOverview::on_context_menu_edit_relationships), table ));
451
m_context_menu->popup(button, activate_time);
455
void Dialog_RelationshipsOverview::setup_context_menu()
457
m_context_menu_action_group = Gtk::ActionGroup::create();
459
m_context_menu_action_group->add(Gtk::Action::create("ContextMenu", "Context Menu") );
461
m_action_edit_fields = Gtk::Action::create("ContextEditFields", _("Edit _Fields"));
462
m_context_menu_action_group->add(m_action_edit_fields);
464
m_action_edit_relationships = Gtk::Action::create("ContextEditRelationships", _("Edit _Relationships"));
465
m_context_menu_action_group->add(m_action_edit_relationships);
467
m_context_menu_uimanager = Gtk::UIManager::create();
468
m_context_menu_uimanager->insert_action_group(m_context_menu_action_group);
470
#ifdef GLIBMM_EXCEPTIONS_ENABLED
474
Glib::ustring ui_info =
476
" <popup name='ContextMenu'>"
477
" <menuitem action='ContextEditFields'/>"
478
" <menuitem action='ContextEditRelationships'/>"
482
#ifdef GLIBMM_EXCEPTIONS_ENABLED
483
m_context_menu_uimanager->add_ui_from_string(ui_info);
485
catch(const Glib::Error& ex)
487
std::cerr << "building menus failed: " << ex.what();
490
std::auto_ptr<Glib::Error> error;
491
m_context_menu_uimanager->add_ui_from_string(ui_info, error);
492
if(error.get() != NULL)
494
std::cerr << "building menus failed: " << error->what();
499
m_context_menu = dynamic_cast<Gtk::Menu*>( m_context_menu_uimanager->get_widget("/ContextMenu") );
502
void Dialog_RelationshipsOverview::on_context_menu_edit_fields(const Glib::RefPtr<CanvasGroupDbTable>& table)
504
App_Glom* pApp = App_Glom::get_application();
507
pApp->do_menu_developer_fields(*this, table->get_table_name());
513
void Dialog_RelationshipsOverview::on_context_menu_edit_relationships(const Glib::RefPtr<CanvasGroupDbTable>& table)
515
App_Glom* pApp = App_Glom::get_application();
518
pApp->do_menu_developer_relationships(*this, table->get_table_name());
525
void Dialog_RelationshipsOverview::on_scroll_value_changed()
527
if(!m_scrolledwindow_canvas)
530
double width = m_scrolledwindow_canvas->get_hadjustment()->get_page_size();
531
double height = m_scrolledwindow_canvas->get_vadjustment()->get_page_size();
532
//double x = m_scrolledwindow_canvas->get_hadjustment()->get_value();
533
//double y = m_scrolledwindow_canvas->get_vadjustment()->get_value();
535
//Make sure that the canvas bounds are as big as the scrollable area:
538
double old_right = 0;
539
double old_bottom = 0;
540
m_canvas.get_bounds(old_left, old_top, old_right, old_bottom);
542
const double old_height = old_bottom - old_top;
543
const double old_width = old_right - old_left;
545
if( (width > old_width) ||
546
(height > old_height) )
548
m_canvas.set_bounds(0, 0, width, height);