From 27e791212dcad23b1fbd1d115c35bcbe0be18240 Mon Sep 17 00:00:00 2001 From: Jim Evins Date: Mon, 14 Oct 2013 22:19:24 -0400 Subject: [PATCH] Initial porting of some core files from VALA/gtk+ to C++/Qt. --- .gitignore | 31 + CMakeLists.txt | 29 + app/BarcodeStyle.cpp | 21 + app/BarcodeStyle.h | 117 ++++ app/CMakeLists.txt | 42 ++ app/Color.cpp | 21 + app/Color.h | 153 +++++ app/ColorNode.cpp | 22 + app/ColorNode.h | 142 +++++ app/LabelModel.cpp | 1247 ++++++++++++++++++++++++++++++++++++++++ app/LabelModel.h | 1215 ++++++++++++++++++++++++++++++++++++++ app/LabelModelItem.cpp | 302 ++++++++++ app/LabelModelItem.h | 483 ++++++++++++++++ app/LabelRegion.cpp | 21 + app/LabelRegion.h | 53 ++ app/MergeField.cpp | 21 + app/MergeField.h | 47 ++ app/MergeRecord.cpp | 22 + app/MergeRecord.h | 58 ++ app/TextNode.cpp | 231 ++++++++ app/TextNode.h | 82 +++ app/qtlabels_main.cpp | 33 ++ 22 files changed, 4393 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 app/BarcodeStyle.cpp create mode 100644 app/BarcodeStyle.h create mode 100644 app/CMakeLists.txt create mode 100644 app/Color.cpp create mode 100644 app/Color.h create mode 100644 app/ColorNode.cpp create mode 100644 app/ColorNode.h create mode 100644 app/LabelModel.cpp create mode 100644 app/LabelModel.h create mode 100644 app/LabelModelItem.cpp create mode 100644 app/LabelModelItem.h create mode 100644 app/LabelRegion.cpp create mode 100644 app/LabelRegion.h create mode 100644 app/MergeField.cpp create mode 100644 app/MergeField.h create mode 100644 app/MergeRecord.cpp create mode 100644 app/MergeRecord.h create mode 100644 app/TextNode.cpp create mode 100644 app/TextNode.h create mode 100644 app/qtlabels_main.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a504685 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# My cmake build directory +/build + +# Compiled Object files +*.slo +*.lo +*.o + +# Compiled Dynamic libraries +*.so +*.dylib + +# Compiled Static libraries +*.lai +*.la +*.a + +# +# Ignore artifacts from editors, patching, etc. +# +*~ +*.bak +\#*\# +.\#* +.*.swp +*.orig +*.rej +gmon.out +core +*.safe +*.sav* diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0b5f88f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required (VERSION 2.8) + +project (qtLabels) + + +set (Package_Name "qtLabels") +set (Short_Name "qtLabels") +set (Package_URL "https://github.com/jimevins/qtLabels") + +set (Major_Version "0") +set (Minor_Version "0") +set (Bugfix_Version "0") + +set (Package_Version "${Major_Version}.${Minor_Version}.${Bugfix_Version}") + +set (Unique_Package_Name ${Package_Name}-${API_Version}) + + +find_package(Qt4 REQUIRED) + + +add_definitions (-g) + + +# +# Subdirectories +# +add_subdirectory (app) + diff --git a/app/BarcodeStyle.cpp b/app/BarcodeStyle.cpp new file mode 100644 index 0000000..4395313 --- /dev/null +++ b/app/BarcodeStyle.cpp @@ -0,0 +1,21 @@ +/* BarcodeStyle.cpp + * + * Copyright (C) 2013 Jim Evins + * + * This file is part of qtlabels. + * + * qtlabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtlabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtlabels. If not, see . + */ + +#include "BarcodeStyle.h" diff --git a/app/BarcodeStyle.h b/app/BarcodeStyle.h new file mode 100644 index 0000000..b13a1b5 --- /dev/null +++ b/app/BarcodeStyle.h @@ -0,0 +1,117 @@ +/* BarcodeStyle.h + * + * Copyright (C) 2013 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + +#ifndef qtlabels_BarcodeStyle_h +#define qtlabels_BarcodeStyle_h + +#include +#include + +namespace qtLabels +{ + + class BarcodeStyle + { + + public: + BarcodeStyle () + : m_id( "" ), + m_name( "" ), + m_can_text( false ), + m_text_optional( false ), + m_can_checksum( false ), + m_checksum_optional( false ), + m_default_digits( "" ), + m_can_freeform( false ), + m_prefered_n( 0 ) + { + } + + + BarcodeStyle ( const QString &id, + const QString &name, + bool can_text, + bool text_optional, + bool can_checksum, + bool checksum_optional, + const QString &default_digits, + bool can_freeform, + int prefered_n ) + : m_id( id ), + m_name( name ), + m_can_text( can_text ), + m_text_optional( text_optional ), + m_can_checksum( can_checksum ), + m_checksum_optional( checksum_optional ), + m_default_digits( default_digits ), + m_can_freeform( can_freeform ), + m_prefered_n( prefered_n ) + { + } + + + QString id() { return m_id; } + + QString name() { return m_name; } + + bool can_text() { return m_can_text; } + + bool text_optional() { return m_text_optional; } + + bool can_checksum() { return m_can_checksum; } + + bool checksum_optional() { return m_checksum_optional; } + + QString default_digits() { return m_default_digits; } + + bool can_freeform() { return m_can_freeform; } + + int prefered_n() { return m_prefered_n; } + + + QString example_digits( int n ) + { + if ( m_can_freeform ) + { + return QString( std::max( n, 1 ), QChar('0') ); + } + else + { + return m_default_digits; + } + } + + private: + QString m_id; + QString m_name; + bool m_can_text; + bool m_text_optional; + bool m_can_checksum; + bool m_checksum_optional; + QString m_default_digits; + bool m_can_freeform; + int m_prefered_n; + + }; + + +} + +#endif // qtlabels_BarcodeStyle_h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt new file mode 100644 index 0000000..060072f --- /dev/null +++ b/app/CMakeLists.txt @@ -0,0 +1,42 @@ +cmake_minimum_required (VERSION 2.8) + +project (app CXX) + +set (qtlabels_sources + qtlabels_main.cpp + BarcodeStyle.cpp + Color.cpp + ColorNode.cpp + LabelModel.cpp + LabelModelItem.cpp + LabelRegion.cpp + MergeField.cpp + MergeRecord.cpp + TextNode.cpp +) + +set (qobject_headers + LabelModel.h + LabelModelItem.h +) + +qt4_wrap_cpp (moc_sources ${qobject_headers}) + + +include (${QT_USE_FILE}) + + +include_directories ( +) + +link_directories ( +) + +add_executable (qtlabels ${qtlabels_sources} ${moc_sources}) + +target_link_libraries (qtlabels + ${QT_LIBRARIES} +) + + +install (TARGETS qtlabels RUNTIME DESTINATION bin) diff --git a/app/Color.cpp b/app/Color.cpp new file mode 100644 index 0000000..6c1fabd --- /dev/null +++ b/app/Color.cpp @@ -0,0 +1,21 @@ +/* Color.cpp + * + * Copyright (C) 2011 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + +#include "Color.h" diff --git a/app/Color.h b/app/Color.h new file mode 100644 index 0000000..84cd48d --- /dev/null +++ b/app/Color.h @@ -0,0 +1,153 @@ +/* Color.h + * + * Copyright (C) 2011 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + +#ifndef qtlabels_Color_h +#define qtlabels_Color_h + +#include + + +namespace qtLabels +{ + + class Color + { + + public: + /* double RGBA Constructor */ + Color( double r = 0, double g = 0, double b = 0, double a = 1 ) + : m_r(r), m_g(g), m_b(b), m_a(a) + { + } + + + static Color from_byte_rgba( uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255 ) + { + return Color( r/255.0, g/255.0, b/255.0, a/255.0 ); + } + + + static Color from_legacy_color( uint32_t c ) + { + return Color( ((c>>24) & 0xff) / 255.0, + ((c>>16) & 0xff) / 255.0, + ((c>>8) & 0xff) / 255.0, + ((c) & 0xff) / 255.0 ); + } + + + static Color none() + { + return Color( 0, 0, 0, 0 ); + } + + + static Color black() + { + return Color( 0, 0, 0, 1 ); + } + + + static Color white() + { + return Color( 1, 1, 1, 1 ); + } + + + /* Muliply by opacity. Create new color by applying opacity. */ + Color operator*( double opacity ) const + { + return Color( m_r, m_g, m_b, m_a * opacity ); + } + + + uint32_t to_legacy_color() + { + uint32_t c = (((uint32_t)(m_r*255) & 0xff) << 24) | + (((uint32_t)(m_g*255) & 0xff) << 16) | + (((uint32_t)(m_b*255) & 0xff) << 8) | + (((uint32_t)(m_a*255) & 0xff)); + return c; + } + + + bool operator==( const Color &c ) + { + return ( (m_r == c.m_r) && + (m_g == c.m_g) && + (m_b == c.m_b) && + (m_a == c.m_a) ); + } + + + bool operator!=( const Color &c ) + { + return ( (m_r != c.m_r) || + (m_g != c.m_g) || + (m_b != c.m_b) || + (m_a != c.m_a) ); + } + + + bool has_alpha() + { + return ( m_a != 0 ); + } + + + /* + * Red (r) Property + */ + double r( void ) { return m_r; } + void r( double value ) { m_r = value; } + + + /* + * Green (g) Property + */ + double g( void ) { return m_g; } + void g( double value ) { m_g = value; } + + + /* + * Blue (b) Property + */ + double b( void ) { return m_b; } + void b( double value ) { m_b = value; } + + + /* + * Alpha (a) Property + */ + double a( void ) { return m_a; } + void a( double value ) { m_a = value; } + + + private: + double m_r; + double m_g; + double m_b; + double m_a; + + }; + +} + +#endif // qtlabels_Color_h diff --git a/app/ColorNode.cpp b/app/ColorNode.cpp new file mode 100644 index 0000000..d109055 --- /dev/null +++ b/app/ColorNode.cpp @@ -0,0 +1,22 @@ +/* ColorNode.cpp + * + * Copyright (C) 2013 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + +#include "ColorNode.h" + diff --git a/app/ColorNode.h b/app/ColorNode.h new file mode 100644 index 0000000..51f98d4 --- /dev/null +++ b/app/ColorNode.h @@ -0,0 +1,142 @@ +/* ColorNode.h + * + * Copyright (C) 2013 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + +#ifndef qtlabels_ColorNode_h +#define qtlabels_ColorNode_h + +#include + +#include "Color.h" + + +namespace qtLabels +{ + + class ColorNode + { + + public: + ColorNode() + : m_field_flag(false), m_color(Color::none()), m_key("") + { + } + + + ColorNode( bool field_flag, Color &color, QString &key ) + : m_field_flag(field_flag), m_color(color), m_key(key) + { + } + + + ColorNode( const Color &color ) + : m_field_flag(false), m_color(color), m_key("") + { + } + + + ColorNode( QString &key ) + : m_field_flag(true), m_color(Color::none()), m_key(key) + { + } + + + bool operator==( const ColorNode &cn ) + { + return ( (m_field_flag == cn.m_field_flag) && + (m_color == cn.m_color) && + (m_key == cn.m_key) ); + } + + + bool operator!=( const ColorNode &cn ) + { + return ( (m_field_flag != cn.m_field_flag) || + (m_color != cn.m_color) || + (m_key != cn.m_key) ); + } + + +#if TODO + Color expand( MergeRecord? record ) + { + if ( field_flag ) + { + if ( record == null ) + { + return Color.none(); + } + else + { + string? text = record.eval_key( key ); + if ( text != null ) + { + Gdk.Color gdk_color = Gdk.Color(); + if ( Gdk.Color.parse( text, out gdk_color ) ) + { + Color color = Color.from_gdk_color( gdk_color ); + return color; + } + else + { + return Color.none(); + } + } + else + { + return Color.none(); + } + } + } + else + { + return color; + } + } +#endif + + + /* + * field flag property + */ + bool field_flag( void ) { return m_field_flag; } + + + /* + * color property + */ + Color color( void ) { return m_color; } + + + /* + * key property + */ + QString key( void ) { return m_key; } + + + private: + bool m_field_flag; + Color m_color; + QString m_key; + + }; + +} + +#endif // qtlabels_ColorNode_h diff --git a/app/LabelModel.cpp b/app/LabelModel.cpp new file mode 100644 index 0000000..7e5b76e --- /dev/null +++ b/app/LabelModel.cpp @@ -0,0 +1,1247 @@ +/* LabelModel.cpp + * + * Copyright (C) 2013 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + +#include "LabelModel.h" + +namespace qtLabels +{ + + /** + * Default constructor. + */ + LabelModel::LabelModel() + { + } + + + /** + * Add item. + */ + void LabelModel::add_item( LabelModelItem &item ) + { + m_item_list.push_back( &item ); + } + + + /** + * Select item. + */ + void LabelModel::select_item( LabelModelItem &item ) + { + item.select(); + + emit selection_changed(); + } + + + /** + * Unselect item. + */ + void LabelModel::unselect_item( LabelModelItem &item ) + { + item.unselect(); + + emit selection_changed(); + } + + + /** + * Select all items. + */ + void LabelModel::select_all( void ) + { + list::iterator i_item; + + for ( i_item = m_item_list.begin(); i_item != m_item_list.end(); i_item++ ) + { + (*i_item)->select(); + } + + emit selection_changed(); + } + + + /** + * Unselect item all items. + */ + void LabelModel::unselect_all( void ) + { + list::iterator i_item; + + for ( i_item = m_item_list.begin(); i_item != m_item_list.end(); i_item++ ) + { + (*i_item)->unselect(); + } + + emit selection_changed(); + } + +} + +/////////////////////////////////////////////////////////////////////////////// +#if 0 +#include "LabelModelObject.h" + +using namespace std; + +namespace qtLabels +{ + + public class LabelModel : QObject + { + signals: + void name_changed(); + void selection_changed(); + void modified_changed(); + void size_changed(); + void changed(); + void merge_changed(); + + + public: + list object_list; + + // PixbufCache pixbuf_cache; + // SvgCache svg_cache; + + + /** + * Filename + */ + public string? filename + { + get { return _filename; } + + set + { + if ( _filename != value ) + { + _filename = value; + name_changed(); + } + } + } + private string? _filename; + + + /** + * Compression mode ( 0 = no compression, 9 = max compression ) + */ + public int compression + { + get { return _compression; } + + set + { + if ( (value < 0) && (value > 9) ) + { + warning( "Compression mode out of range." ); + _compression = 9; + } + else + { + _compression = value; + } + } + } + private int _compression = 9; + + + /** + * Modified flag + */ + public bool modified + { + get { return _modified; } + + set + { + if ( _modified != value ) + { + _modified = value; + if ( !_modified ) + { + time_stamp.get_current_time(); + } + modified_changed(); + } + } + } + private bool _modified; + + public TimeVal time_stamp { get; private set; } + + + /** + * Template + */ + public Template template + { + get { return _template; } + + set + { + if ( _template != value ) + { + _template = value; + changed(); + size_changed(); + template_history.add_name( template.name ); + modified = true; + } + } + } + private Template _template; + + + /** + * Rotate + */ + public bool rotate + { + get { return _rotate; } + + set + { + if ( _rotate != value ) + { + _rotate = value; + changed(); + size_changed(); + modified = true; + } + } + } + private bool _rotate; + + + /** + * Merge + */ + public Merge merge + { + get { return _merge; } + + set + { + if ( _merge != value ) + { + _merge = value; + _merge.source_changed.connect( on_merge_source_changed ); + changed(); + merge_changed(); + modified = true; + } + } + } + private Merge _merge; + + + /* Default object text properties */ + public string default_font_family { get; set; } + public double default_font_size { get; set; } + public Pango.Weight default_font_weight { get; set; default=Pango.Weight.NORMAL; } + public bool default_font_italic_flag { get; set; } + public Color default_text_color { get; set; } + public Pango.Alignment default_text_alignment { get; set; } + public double default_text_line_spacing { get; set; } + + /* Default object line properties */ + public double default_line_width { get; set; } + public Color default_line_color { get; set; } + + /* Default object fill properties */ + public Color default_fill_color { get; set; } + + + private TemplateHistory template_history; + + private static int untitled_count; + private int untitled_instance; + + private bool selection_op_flag; + private bool delayed_change_flag; + + + public Label() + { + _merge = MergeBackends.create_merge( "None" ); + + template_history = new TemplateHistory( 5 ); + + pixbuf_cache = new PixbufCache(); + svg_cache = new SvgCache(); + + // TODO: Set default properties from user prefs + } + + + + public string get_short_name() + { + if ( filename == null ) + { + + if ( untitled_instance == 0 ) + { + untitled_instance = ++untitled_count; + } + + return "%s %d".printf( _("Untitled"), untitled_instance ); + + } + else + { + + string base_name = Path.get_basename( filename ); + try + { + Regex ext_pattern = new Regex( "\\.glabels$" ); + string short_name = ext_pattern.replace( base_name, -1, 0, "" ); + return short_name; + } + catch ( RegexError e ) + { + warning( "%s", e.message ); + return base_name; + } + + + } + } + + + public bool is_untitled() + { + return filename == null; + } + + + public void get_size( out double w, + out double h ) + { + if ( template == null ) + { + w = 0; + h = 0; + return; + } + + TemplateFrame frame = template.frames.first().data; + + if ( !rotate ) + { + frame.get_size( out w, out h ); + } + else + { + frame.get_size( out h, out w ); + } + } + + + public void add_object( LabelObject object ) + { + object.parent = this; + object_list.append( object ); + + object.changed.connect( on_object_changed ); + object.moved.connect( on_object_moved ); + + changed(); + modified = true; + } + + + public void delete_object( LabelObject object ) + { + unselect_object( object ); + + object_list.remove( object ); + + object.changed.disconnect( on_object_changed ); + object.moved.disconnect( on_object_moved ); + + changed(); + modified = true; + } + + + private void on_object_changed() + { + schedule_or_emit_changed_signal(); + } + + + private void on_object_moved() + { + schedule_or_emit_changed_signal(); + } + + + private void on_merge_source_changed() + { + changed(); + } + + + public void draw( Cairo.Context cr, + bool in_editor, + MergeRecord? record ) + { + foreach ( LabelObject object in object_list ) + { + object.draw( cr, in_editor, record ); + } + } + + + public LabelObject? object_at( Cairo.Context cr, + double x_pixels, + double y_pixels ) + { + for ( unowned List? p = object_list.last(); p != null; p = p.prev ) + { + LabelObject object = p.data; + + if ( object.is_located_at( cr, x_pixels, y_pixels ) ) + { + return object; + } + } + + return null; + } + + + public Handle? handle_at( Cairo.Context cr, + double x_pixels, + double y_pixels ) + { + for ( unowned List? p = object_list.last(); p != null; p = p.prev ) + { + LabelObject object = p.data; + + if ( object.is_selected() ) + { + Handle? handle = object.handle_at( cr, x_pixels, y_pixels ); + + if ( handle != null ) + { + return handle; + } + } + } + + return null; + } + + + public void select_object( LabelObject object ) + { + object.select(); + selection_changed(); + } + + + public void unselect_object( LabelObject object ) + { + object.unselect(); + selection_changed(); + } + + + public void select_all() + { + foreach ( LabelObject object in object_list ) + { + object.select(); + } + selection_changed(); + } + + + public void unselect_all() + { + foreach ( LabelObject object in object_list ) + { + object.unselect(); + } + selection_changed(); + } + + + public void select_region( LabelRegion region ) + { + double r_x1 = double.min( region.x1, region.x2 ); + double r_y1 = double.min( region.y1, region.y2 ); + double r_x2 = double.max( region.x1, region.x2 ); + double r_y2 = double.max( region.y1, region.y2 ); + + foreach ( LabelObject object in object_list ) + { + LabelRegion obj_extent = object.get_extent(); + + if ( (obj_extent.x1 >= r_x1) && + (obj_extent.x2 <= r_x2) && + (obj_extent.y1 >= r_y1) && + (obj_extent.y2 <= r_y2) ) + { + object.select(); + } + } + selection_changed(); + } + + + public bool is_selection_empty() + { + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + return false; + } + } + return true; + } + + + public bool is_selection_atomic() + { + int n_selected = 0; + + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + n_selected++; + if ( n_selected > 1 ) + { + return false; + } + } + } + return (n_selected == 1); + } + + + public LabelObject? get_1st_selected_object() + { + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + return object; + } + } + return null; + } + + + public List get_selection_list() + { + List selection_list = null; + + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + selection_list.append( object ); + } + } + return selection_list; + } + + + public bool can_selection_text() + { + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() && object.can_text() ) + { + return true; + } + } + return false; + } + + + public bool can_selection_fill() + { + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() && object.can_fill() ) + { + return true; + } + } + return false; + } + + + public bool can_selection_line_color() + { + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() && object.can_line_color() ) + { + return true; + } + } + return false; + } + + + public bool can_selection_line_width() + { + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() && object.can_line_width() ) + { + return true; + } + } + return false; + } + + + private void schedule_or_emit_changed_signal() + { + if ( selection_op_flag ) + { + delayed_change_flag = true; + } + else + { + modified = true; + changed(); + } + } + + + private void begin_selection_op() + { + selection_op_flag = true; + } + + + private void end_selection_op() + { + selection_op_flag = false; + if ( delayed_change_flag ) + { + delayed_change_flag = false; + changed(); + modified = true; + } + } + + + public void delete_selection() + { + List selection_list = get_selection_list(); + + foreach ( LabelObject object in selection_list ) + { + delete_object( object ); + } + + changed(); + modified = true; + } + + + public void raise_selection_to_top() + { + List selection_list = get_selection_list(); + + foreach ( LabelObject object in selection_list ) + { + object_list.remove( object ); + } + + /* Move to end of list, representing front most object */ + foreach ( LabelObject object in selection_list ) + { + object_list.append( object ); + } + + changed(); + modified = true; + } + + + public void lower_selection_to_bottom() + { + List selection_list = get_selection_list(); + + foreach ( LabelObject object in selection_list ) + { + object_list.remove( object ); + } + + /* Move to front of list, representing rear most object */ + selection_list.reverse(); + foreach ( LabelObject object in selection_list ) + { + object_list.prepend( object ); + } + + changed(); + modified = true; + } + + + public void rotate_selection( double theta_degs ) + { + begin_selection_op(); + + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.rotate( theta_degs ); + } + } + + end_selection_op(); + } + + + public void rotate_selection_left() + { + begin_selection_op(); + + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.rotate( -90.0 ); + } + } + + end_selection_op(); + } + + + public void rotate_selection_right() + { + begin_selection_op(); + + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.rotate( 90.0 ); + } + } + + end_selection_op(); + } + + + public void flip_selection_horiz() + { + begin_selection_op(); + + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.flip_horiz(); + } + } + + end_selection_op(); + } + + + public void flip_selection_vert() + { + begin_selection_op(); + + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.flip_vert(); + } + } + + end_selection_op(); + } + + + public void align_selection_left() + { + if ( is_selection_empty() || is_selection_atomic() ) + { + return; + } + + begin_selection_op(); + + List selection_list = get_selection_list(); + + /* Find left most edge. */ + LabelRegion obj_extent = selection_list.first().data.get_extent(); + double x1_min = obj_extent.x1; + foreach ( LabelObject object in selection_list.nth(1) ) + { + obj_extent = object.get_extent(); + if ( obj_extent.x1 < x1_min ) x1_min = obj_extent.x1; + } + + /* Now adjust the object positions to line up the left edges. */ + foreach ( LabelObject object in selection_list ) + { + obj_extent = object.get_extent(); + double dx = x1_min - obj_extent.x1; + object.set_position_relative( dx, 0 ); + } + + end_selection_op(); + } + + + public void align_selection_right() + { + if ( is_selection_empty() || is_selection_atomic() ) + { + return; + } + + begin_selection_op(); + + List selection_list = get_selection_list(); + + /* Find right most edge. */ + LabelRegion obj_extent = selection_list.first().data.get_extent(); + double x2_max = obj_extent.x2; + foreach ( LabelObject object in selection_list.nth(1) ) + { + obj_extent = object.get_extent(); + if ( obj_extent.x2 > x2_max ) x2_max = obj_extent.x2; + } + + /* Now adjust the object positions to line up the right edges. */ + foreach ( LabelObject object in selection_list ) + { + obj_extent = object.get_extent(); + double dx = x2_max - obj_extent.x2; + object.set_position_relative( dx, 0 ); + } + + end_selection_op(); + } + + + public void align_selection_hcenter() + { + if ( is_selection_empty() || is_selection_atomic() ) + { + return; + } + + begin_selection_op(); + + List selection_list = get_selection_list(); + LabelRegion obj_extent; + + /* Find average center of objects. */ + double xsum = 0; + int n = 0; + foreach ( LabelObject object in selection_list ) + { + obj_extent = object.get_extent(); + xsum += (obj_extent.x1 + obj_extent.x2) / 2.0; + n++; + } + double xavg = xsum / n; + + /* find center of object closest to average center */ + obj_extent = selection_list.first().data.get_extent(); + double xcenter = (obj_extent.x1 + obj_extent.x2) / 2.0; + double dxmin = Math.fabs( xavg - xcenter ); + foreach ( LabelObject object in selection_list.nth(1) ) + { + obj_extent = object.get_extent(); + double dx = Math.fabs( xavg - (obj_extent.x1 + obj_extent.x2)/2.0 ); + if ( dx < dxmin ) + { + dxmin = dx; + xcenter = (obj_extent.x1 + obj_extent.x2) / 2.0; + } + } + + /* Now adjust the object positions to line up with this center. */ + foreach ( LabelObject object in selection_list ) + { + obj_extent = object.get_extent(); + double dx = xcenter - (obj_extent.x1 + obj_extent.x2)/2.0; + object.set_position_relative( dx, 0 ); + } + + end_selection_op(); + } + + + public void align_selection_top() + { + if ( is_selection_empty() || is_selection_atomic() ) + { + return; + } + + begin_selection_op(); + + List selection_list = get_selection_list(); + + /* Find top most edge. */ + LabelRegion obj_extent = selection_list.first().data.get_extent(); + double y1_min = obj_extent.y1; + foreach ( LabelObject object in selection_list.nth(1) ) + { + obj_extent = object.get_extent(); + if ( obj_extent.y1 < y1_min ) y1_min = obj_extent.y1; + } + + /* Now adjust the object positions to line up the top edges. */ + foreach ( LabelObject object in selection_list ) + { + obj_extent = object.get_extent(); + double dy = y1_min - obj_extent.y1; + object.set_position_relative( 0, dy ); + } + + end_selection_op(); + } + + + public void align_selection_bottom() + { + if ( is_selection_empty() || is_selection_atomic() ) + { + return; + } + + begin_selection_op(); + + List selection_list = get_selection_list(); + + /* Find bottom most edge. */ + LabelRegion obj_extent = selection_list.first().data.get_extent(); + double y2_max = obj_extent.y2; + foreach ( LabelObject object in selection_list.nth(1) ) + { + obj_extent = object.get_extent(); + if ( obj_extent.y2 > y2_max ) y2_max = obj_extent.y2; + } + + /* Now adjust the object positions to line up the bottom edges. */ + foreach ( LabelObject object in selection_list ) + { + obj_extent = object.get_extent(); + double dy = y2_max - obj_extent.y2; + object.set_position_relative( 0, dy ); + } + + end_selection_op(); + } + + + public void align_selection_vcenter() + { + if ( is_selection_empty() || is_selection_atomic() ) + { + return; + } + + begin_selection_op(); + + List selection_list = get_selection_list(); + LabelRegion obj_extent; + + /* Find average center of objects. */ + double ysum = 0; + int n = 0; + foreach ( LabelObject object in selection_list ) + { + obj_extent = object.get_extent(); + ysum += (obj_extent.y1 + obj_extent.y2) / 2.0; + n++; + } + double yavg = ysum / n; + + /* find center of object closest to average center */ + obj_extent = selection_list.first().data.get_extent(); + double ycenter = (obj_extent.y1 + obj_extent.y2) / 2.0; + double dymin = Math.fabs( yavg - ycenter ); + foreach ( LabelObject object in selection_list.nth(1) ) + { + obj_extent = object.get_extent(); + double dy = Math.fabs( yavg - (obj_extent.y1 + obj_extent.y2)/2.0 ); + if ( dy < dymin ) + { + dymin = dy; + ycenter = (obj_extent.y1 + obj_extent.y2) / 2.0; + } + } + + /* Now adjust the object positions to line up with this center. */ + foreach ( LabelObject object in selection_list ) + { + obj_extent = object.get_extent(); + double dy = ycenter - (obj_extent.y1 + obj_extent.y2)/2.0; + object.set_position_relative( 0, dy ); + } + + end_selection_op(); + } + + + public void center_selection_horiz() + { + begin_selection_op(); + + double w, h; + get_size( out w, out h ); + double x_label_center = w / 2.0; + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + LabelRegion obj_extent = object.get_extent(); + double x_obj_center = (obj_extent.x1 + obj_extent.x2) / 2.0; + double dx = x_label_center - x_obj_center; + object.set_position_relative( dx, 0 ); + } + } + + end_selection_op(); + } + + + public void center_selection_vert() + { + begin_selection_op(); + + double w, h; + get_size( out w, out h ); + double y_label_center = h / 2.0; + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + LabelRegion obj_extent = object.get_extent(); + double y_obj_center = (obj_extent.y1 + obj_extent.y2) / 2.0; + double dy = y_label_center - y_obj_center; + object.set_position_relative( 0, dy ); + } + } + + end_selection_op(); + } + + + public void move_selection( double dx, + double dy ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.set_position_relative( dx, dy ); + } + } + + end_selection_op(); + } + + + public void set_selection_font_family( string font_family ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.font_family = font_family; + } + } + + end_selection_op(); + } + + + public void set_selection_font_size( double font_size ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.font_size = font_size; + } + } + + end_selection_op(); + } + + + public void set_selection_font_weight( Pango.Weight font_weight ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.font_weight = font_weight; + } + } + + end_selection_op(); + } + + + public void set_selection_font_italic_flag( bool font_italic_flag ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.font_italic_flag = font_italic_flag; + } + } + + end_selection_op(); + } + + + public void set_selection_text_alignment( Pango.Alignment text_alignment ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.text_alignment = text_alignment; + } + } + + end_selection_op(); + } + + + public void set_selection_text_line_spacing( double text_line_spacing ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.text_line_spacing = text_line_spacing; + } + } + + end_selection_op(); + } + + + public void set_selection_text_color( ColorNode text_color_node ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.text_color_node = text_color_node; + } + } + + end_selection_op(); + } + + + public void set_selection_line_width( double line_width ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.line_width = line_width; + } + } + + end_selection_op(); + } + + + public void set_selection_line_color( ColorNode line_color_node ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.line_color_node = line_color_node; + } + } + + end_selection_op(); + } + + + public void set_selection_fill_color( ColorNode fill_color_node ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.fill_color_node = fill_color_node; + } + } + + end_selection_op(); + } + + + } + +} +#endif diff --git a/app/LabelModel.h b/app/LabelModel.h new file mode 100644 index 0000000..3fa70e9 --- /dev/null +++ b/app/LabelModel.h @@ -0,0 +1,1215 @@ +/* LabelModel.h + * + * Copyright (C) 2013 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + +#ifndef qtlabels_LabelModel_h +#define qtlabels_LabelModel_h + +#include +#include + +#include "LabelModelItem.h" + +using namespace std; + +namespace qtLabels +{ + + class LabelModel : public QObject + { + Q_OBJECT + + public: + LabelModel(); + virtual ~LabelModel() {} + + signals: + void selection_changed(); + + public: + void add_item( LabelModelItem &item ); + + void select_item( LabelModelItem &item ); + + void unselect_item( LabelModelItem &item ); + + void select_all( void ); + + void unselect_all( void ); + + + private: + list m_item_list; + + }; + +} + +#endif // qtlabels_LabelModel_h + +/////////////////////////////////////////////////////////////////////////////// +#if 0 +#include "LabelModelObject.h" + +using namespace std; + +namespace qtLabels +{ + + public class LabelModel : QObject + { + signals: + void name_changed(); + void selection_changed(); + void modified_changed(); + void size_changed(); + void changed(); + void merge_changed(); + + + public: + list object_list; + + // PixbufCache pixbuf_cache; + // SvgCache svg_cache; + + + /** + * Filename + */ + public string? filename + { + get { return _filename; } + + set + { + if ( _filename != value ) + { + _filename = value; + name_changed(); + } + } + } + private string? _filename; + + + /** + * Compression mode ( 0 = no compression, 9 = max compression ) + */ + public int compression + { + get { return _compression; } + + set + { + if ( (value < 0) && (value > 9) ) + { + warning( "Compression mode out of range." ); + _compression = 9; + } + else + { + _compression = value; + } + } + } + private int _compression = 9; + + + /** + * Modified flag + */ + public bool modified + { + get { return _modified; } + + set + { + if ( _modified != value ) + { + _modified = value; + if ( !_modified ) + { + time_stamp.get_current_time(); + } + modified_changed(); + } + } + } + private bool _modified; + + public TimeVal time_stamp { get; private set; } + + + /** + * Template + */ + public Template template + { + get { return _template; } + + set + { + if ( _template != value ) + { + _template = value; + changed(); + size_changed(); + template_history.add_name( template.name ); + modified = true; + } + } + } + private Template _template; + + + /** + * Rotate + */ + public bool rotate + { + get { return _rotate; } + + set + { + if ( _rotate != value ) + { + _rotate = value; + changed(); + size_changed(); + modified = true; + } + } + } + private bool _rotate; + + + /** + * Merge + */ + public Merge merge + { + get { return _merge; } + + set + { + if ( _merge != value ) + { + _merge = value; + _merge.source_changed.connect( on_merge_source_changed ); + changed(); + merge_changed(); + modified = true; + } + } + } + private Merge _merge; + + + /* Default object text properties */ + public string default_font_family { get; set; } + public double default_font_size { get; set; } + public Pango.Weight default_font_weight { get; set; default=Pango.Weight.NORMAL; } + public bool default_font_italic_flag { get; set; } + public Color default_text_color { get; set; } + public Pango.Alignment default_text_alignment { get; set; } + public double default_text_line_spacing { get; set; } + + /* Default object line properties */ + public double default_line_width { get; set; } + public Color default_line_color { get; set; } + + /* Default object fill properties */ + public Color default_fill_color { get; set; } + + + private TemplateHistory template_history; + + private static int untitled_count; + private int untitled_instance; + + private bool selection_op_flag; + private bool delayed_change_flag; + + + public Label() + { + _merge = MergeBackends.create_merge( "None" ); + + template_history = new TemplateHistory( 5 ); + + pixbuf_cache = new PixbufCache(); + svg_cache = new SvgCache(); + + // TODO: Set default properties from user prefs + } + + + + public string get_short_name() + { + if ( filename == null ) + { + + if ( untitled_instance == 0 ) + { + untitled_instance = ++untitled_count; + } + + return "%s %d".printf( _("Untitled"), untitled_instance ); + + } + else + { + + string base_name = Path.get_basename( filename ); + try + { + Regex ext_pattern = new Regex( "\\.glabels$" ); + string short_name = ext_pattern.replace( base_name, -1, 0, "" ); + return short_name; + } + catch ( RegexError e ) + { + warning( "%s", e.message ); + return base_name; + } + + + } + } + + + public bool is_untitled() + { + return filename == null; + } + + + public void get_size( out double w, + out double h ) + { + if ( template == null ) + { + w = 0; + h = 0; + return; + } + + TemplateFrame frame = template.frames.first().data; + + if ( !rotate ) + { + frame.get_size( out w, out h ); + } + else + { + frame.get_size( out h, out w ); + } + } + + + public void add_object( LabelObject object ) + { + object.parent = this; + object_list.append( object ); + + object.changed.connect( on_object_changed ); + object.moved.connect( on_object_moved ); + + changed(); + modified = true; + } + + + public void delete_object( LabelObject object ) + { + unselect_object( object ); + + object_list.remove( object ); + + object.changed.disconnect( on_object_changed ); + object.moved.disconnect( on_object_moved ); + + changed(); + modified = true; + } + + + private void on_object_changed() + { + schedule_or_emit_changed_signal(); + } + + + private void on_object_moved() + { + schedule_or_emit_changed_signal(); + } + + + private void on_merge_source_changed() + { + changed(); + } + + + public void draw( Cairo.Context cr, + bool in_editor, + MergeRecord? record ) + { + foreach ( LabelObject object in object_list ) + { + object.draw( cr, in_editor, record ); + } + } + + + public LabelObject? object_at( Cairo.Context cr, + double x_pixels, + double y_pixels ) + { + for ( unowned List? p = object_list.last(); p != null; p = p.prev ) + { + LabelObject object = p.data; + + if ( object.is_located_at( cr, x_pixels, y_pixels ) ) + { + return object; + } + } + + return null; + } + + + public Handle? handle_at( Cairo.Context cr, + double x_pixels, + double y_pixels ) + { + for ( unowned List? p = object_list.last(); p != null; p = p.prev ) + { + LabelObject object = p.data; + + if ( object.is_selected() ) + { + Handle? handle = object.handle_at( cr, x_pixels, y_pixels ); + + if ( handle != null ) + { + return handle; + } + } + } + + return null; + } + + + public void select_object( LabelObject object ) + { + object.select(); + selection_changed(); + } + + + public void unselect_object( LabelObject object ) + { + object.unselect(); + selection_changed(); + } + + + public void select_all() + { + foreach ( LabelObject object in object_list ) + { + object.select(); + } + selection_changed(); + } + + + public void unselect_all() + { + foreach ( LabelObject object in object_list ) + { + object.unselect(); + } + selection_changed(); + } + + + public void select_region( LabelRegion region ) + { + double r_x1 = double.min( region.x1, region.x2 ); + double r_y1 = double.min( region.y1, region.y2 ); + double r_x2 = double.max( region.x1, region.x2 ); + double r_y2 = double.max( region.y1, region.y2 ); + + foreach ( LabelObject object in object_list ) + { + LabelRegion obj_extent = object.get_extent(); + + if ( (obj_extent.x1 >= r_x1) && + (obj_extent.x2 <= r_x2) && + (obj_extent.y1 >= r_y1) && + (obj_extent.y2 <= r_y2) ) + { + object.select(); + } + } + selection_changed(); + } + + + public bool is_selection_empty() + { + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + return false; + } + } + return true; + } + + + public bool is_selection_atomic() + { + int n_selected = 0; + + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + n_selected++; + if ( n_selected > 1 ) + { + return false; + } + } + } + return (n_selected == 1); + } + + + public LabelObject? get_1st_selected_object() + { + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + return object; + } + } + return null; + } + + + public List get_selection_list() + { + List selection_list = null; + + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + selection_list.append( object ); + } + } + return selection_list; + } + + + public bool can_selection_text() + { + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() && object.can_text() ) + { + return true; + } + } + return false; + } + + + public bool can_selection_fill() + { + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() && object.can_fill() ) + { + return true; + } + } + return false; + } + + + public bool can_selection_line_color() + { + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() && object.can_line_color() ) + { + return true; + } + } + return false; + } + + + public bool can_selection_line_width() + { + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() && object.can_line_width() ) + { + return true; + } + } + return false; + } + + + private void schedule_or_emit_changed_signal() + { + if ( selection_op_flag ) + { + delayed_change_flag = true; + } + else + { + modified = true; + changed(); + } + } + + + private void begin_selection_op() + { + selection_op_flag = true; + } + + + private void end_selection_op() + { + selection_op_flag = false; + if ( delayed_change_flag ) + { + delayed_change_flag = false; + changed(); + modified = true; + } + } + + + public void delete_selection() + { + List selection_list = get_selection_list(); + + foreach ( LabelObject object in selection_list ) + { + delete_object( object ); + } + + changed(); + modified = true; + } + + + public void raise_selection_to_top() + { + List selection_list = get_selection_list(); + + foreach ( LabelObject object in selection_list ) + { + object_list.remove( object ); + } + + /* Move to end of list, representing front most object */ + foreach ( LabelObject object in selection_list ) + { + object_list.append( object ); + } + + changed(); + modified = true; + } + + + public void lower_selection_to_bottom() + { + List selection_list = get_selection_list(); + + foreach ( LabelObject object in selection_list ) + { + object_list.remove( object ); + } + + /* Move to front of list, representing rear most object */ + selection_list.reverse(); + foreach ( LabelObject object in selection_list ) + { + object_list.prepend( object ); + } + + changed(); + modified = true; + } + + + public void rotate_selection( double theta_degs ) + { + begin_selection_op(); + + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.rotate( theta_degs ); + } + } + + end_selection_op(); + } + + + public void rotate_selection_left() + { + begin_selection_op(); + + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.rotate( -90.0 ); + } + } + + end_selection_op(); + } + + + public void rotate_selection_right() + { + begin_selection_op(); + + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.rotate( 90.0 ); + } + } + + end_selection_op(); + } + + + public void flip_selection_horiz() + { + begin_selection_op(); + + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.flip_horiz(); + } + } + + end_selection_op(); + } + + + public void flip_selection_vert() + { + begin_selection_op(); + + foreach ( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.flip_vert(); + } + } + + end_selection_op(); + } + + + public void align_selection_left() + { + if ( is_selection_empty() || is_selection_atomic() ) + { + return; + } + + begin_selection_op(); + + List selection_list = get_selection_list(); + + /* Find left most edge. */ + LabelRegion obj_extent = selection_list.first().data.get_extent(); + double x1_min = obj_extent.x1; + foreach ( LabelObject object in selection_list.nth(1) ) + { + obj_extent = object.get_extent(); + if ( obj_extent.x1 < x1_min ) x1_min = obj_extent.x1; + } + + /* Now adjust the object positions to line up the left edges. */ + foreach ( LabelObject object in selection_list ) + { + obj_extent = object.get_extent(); + double dx = x1_min - obj_extent.x1; + object.set_position_relative( dx, 0 ); + } + + end_selection_op(); + } + + + public void align_selection_right() + { + if ( is_selection_empty() || is_selection_atomic() ) + { + return; + } + + begin_selection_op(); + + List selection_list = get_selection_list(); + + /* Find right most edge. */ + LabelRegion obj_extent = selection_list.first().data.get_extent(); + double x2_max = obj_extent.x2; + foreach ( LabelObject object in selection_list.nth(1) ) + { + obj_extent = object.get_extent(); + if ( obj_extent.x2 > x2_max ) x2_max = obj_extent.x2; + } + + /* Now adjust the object positions to line up the right edges. */ + foreach ( LabelObject object in selection_list ) + { + obj_extent = object.get_extent(); + double dx = x2_max - obj_extent.x2; + object.set_position_relative( dx, 0 ); + } + + end_selection_op(); + } + + + public void align_selection_hcenter() + { + if ( is_selection_empty() || is_selection_atomic() ) + { + return; + } + + begin_selection_op(); + + List selection_list = get_selection_list(); + LabelRegion obj_extent; + + /* Find average center of objects. */ + double xsum = 0; + int n = 0; + foreach ( LabelObject object in selection_list ) + { + obj_extent = object.get_extent(); + xsum += (obj_extent.x1 + obj_extent.x2) / 2.0; + n++; + } + double xavg = xsum / n; + + /* find center of object closest to average center */ + obj_extent = selection_list.first().data.get_extent(); + double xcenter = (obj_extent.x1 + obj_extent.x2) / 2.0; + double dxmin = Math.fabs( xavg - xcenter ); + foreach ( LabelObject object in selection_list.nth(1) ) + { + obj_extent = object.get_extent(); + double dx = Math.fabs( xavg - (obj_extent.x1 + obj_extent.x2)/2.0 ); + if ( dx < dxmin ) + { + dxmin = dx; + xcenter = (obj_extent.x1 + obj_extent.x2) / 2.0; + } + } + + /* Now adjust the object positions to line up with this center. */ + foreach ( LabelObject object in selection_list ) + { + obj_extent = object.get_extent(); + double dx = xcenter - (obj_extent.x1 + obj_extent.x2)/2.0; + object.set_position_relative( dx, 0 ); + } + + end_selection_op(); + } + + + public void align_selection_top() + { + if ( is_selection_empty() || is_selection_atomic() ) + { + return; + } + + begin_selection_op(); + + List selection_list = get_selection_list(); + + /* Find top most edge. */ + LabelRegion obj_extent = selection_list.first().data.get_extent(); + double y1_min = obj_extent.y1; + foreach ( LabelObject object in selection_list.nth(1) ) + { + obj_extent = object.get_extent(); + if ( obj_extent.y1 < y1_min ) y1_min = obj_extent.y1; + } + + /* Now adjust the object positions to line up the top edges. */ + foreach ( LabelObject object in selection_list ) + { + obj_extent = object.get_extent(); + double dy = y1_min - obj_extent.y1; + object.set_position_relative( 0, dy ); + } + + end_selection_op(); + } + + + public void align_selection_bottom() + { + if ( is_selection_empty() || is_selection_atomic() ) + { + return; + } + + begin_selection_op(); + + List selection_list = get_selection_list(); + + /* Find bottom most edge. */ + LabelRegion obj_extent = selection_list.first().data.get_extent(); + double y2_max = obj_extent.y2; + foreach ( LabelObject object in selection_list.nth(1) ) + { + obj_extent = object.get_extent(); + if ( obj_extent.y2 > y2_max ) y2_max = obj_extent.y2; + } + + /* Now adjust the object positions to line up the bottom edges. */ + foreach ( LabelObject object in selection_list ) + { + obj_extent = object.get_extent(); + double dy = y2_max - obj_extent.y2; + object.set_position_relative( 0, dy ); + } + + end_selection_op(); + } + + + public void align_selection_vcenter() + { + if ( is_selection_empty() || is_selection_atomic() ) + { + return; + } + + begin_selection_op(); + + List selection_list = get_selection_list(); + LabelRegion obj_extent; + + /* Find average center of objects. */ + double ysum = 0; + int n = 0; + foreach ( LabelObject object in selection_list ) + { + obj_extent = object.get_extent(); + ysum += (obj_extent.y1 + obj_extent.y2) / 2.0; + n++; + } + double yavg = ysum / n; + + /* find center of object closest to average center */ + obj_extent = selection_list.first().data.get_extent(); + double ycenter = (obj_extent.y1 + obj_extent.y2) / 2.0; + double dymin = Math.fabs( yavg - ycenter ); + foreach ( LabelObject object in selection_list.nth(1) ) + { + obj_extent = object.get_extent(); + double dy = Math.fabs( yavg - (obj_extent.y1 + obj_extent.y2)/2.0 ); + if ( dy < dymin ) + { + dymin = dy; + ycenter = (obj_extent.y1 + obj_extent.y2) / 2.0; + } + } + + /* Now adjust the object positions to line up with this center. */ + foreach ( LabelObject object in selection_list ) + { + obj_extent = object.get_extent(); + double dy = ycenter - (obj_extent.y1 + obj_extent.y2)/2.0; + object.set_position_relative( 0, dy ); + } + + end_selection_op(); + } + + + public void center_selection_horiz() + { + begin_selection_op(); + + double w, h; + get_size( out w, out h ); + double x_label_center = w / 2.0; + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + LabelRegion obj_extent = object.get_extent(); + double x_obj_center = (obj_extent.x1 + obj_extent.x2) / 2.0; + double dx = x_label_center - x_obj_center; + object.set_position_relative( dx, 0 ); + } + } + + end_selection_op(); + } + + + public void center_selection_vert() + { + begin_selection_op(); + + double w, h; + get_size( out w, out h ); + double y_label_center = h / 2.0; + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + LabelRegion obj_extent = object.get_extent(); + double y_obj_center = (obj_extent.y1 + obj_extent.y2) / 2.0; + double dy = y_label_center - y_obj_center; + object.set_position_relative( 0, dy ); + } + } + + end_selection_op(); + } + + + public void move_selection( double dx, + double dy ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.set_position_relative( dx, dy ); + } + } + + end_selection_op(); + } + + + public void set_selection_font_family( string font_family ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.font_family = font_family; + } + } + + end_selection_op(); + } + + + public void set_selection_font_size( double font_size ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.font_size = font_size; + } + } + + end_selection_op(); + } + + + public void set_selection_font_weight( Pango.Weight font_weight ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.font_weight = font_weight; + } + } + + end_selection_op(); + } + + + public void set_selection_font_italic_flag( bool font_italic_flag ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.font_italic_flag = font_italic_flag; + } + } + + end_selection_op(); + } + + + public void set_selection_text_alignment( Pango.Alignment text_alignment ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.text_alignment = text_alignment; + } + } + + end_selection_op(); + } + + + public void set_selection_text_line_spacing( double text_line_spacing ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.text_line_spacing = text_line_spacing; + } + } + + end_selection_op(); + } + + + public void set_selection_text_color( ColorNode text_color_node ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.text_color_node = text_color_node; + } + } + + end_selection_op(); + } + + + public void set_selection_line_width( double line_width ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.line_width = line_width; + } + } + + end_selection_op(); + } + + + public void set_selection_line_color( ColorNode line_color_node ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.line_color_node = line_color_node; + } + } + + end_selection_op(); + } + + + public void set_selection_fill_color( ColorNode fill_color_node ) + { + begin_selection_op(); + + foreach( LabelObject object in object_list ) + { + if ( object.is_selected() ) + { + object.fill_color_node = fill_color_node; + } + } + + end_selection_op(); + } + + + } + +} +#endif diff --git a/app/LabelModelItem.cpp b/app/LabelModelItem.cpp new file mode 100644 index 0000000..34ed5f5 --- /dev/null +++ b/app/LabelModelItem.cpp @@ -0,0 +1,302 @@ +/* LabelModelItem.cpp + * + * Copyright (C) 2013 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + +#include "LabelModelItem.h" + + +namespace qtLabels +{ + + /* + * Default constructor. + */ + LabelModelItem::LabelModelItem() + { + m_x0 = 0; + m_y0 = 0; + m_w = 0; + m_h = 0; + m_matrix = QTransform(); + + m_shadow_state = false; + m_shadow_x = 1.3; + m_shadow_y = 1.3; + m_shadow_color_node = ColorNode( Color::black() ); + m_shadow_opacity = 0.5; + + m_selected_flag = false; + } + + + void LabelModelItem::set_position( double x0, + double y0 ) + { + if ( ( m_x0 != x0 ) || ( m_y0 != y0 ) ) + { + m_x0 = x0; + m_y0 = y0; + + moved(); + } + } + + + void LabelModelItem::set_position_relative( double dx, + double dy ) + { + if ( ( dx != 0 ) || ( dy != 0 ) ) + { + m_x0 += dx; + m_y0 += dy; + + moved(); + } + } + + + void LabelModelItem::set_size( double w, + double h ) + { + m_w = w; + m_h = h; + } + + + void LabelModelItem::set_size_honor_aspect( double w, + double h ) + { + double aspect_ratio = m_h / m_w; + + if ( h > (w * aspect_ratio) ) + { + h = w * aspect_ratio; + } + else + { + w = h / aspect_ratio; + } + + if ( ( m_w != w ) || ( m_h != h ) ) + { + m_w = w; + m_h = h; + + changed(); + } + } + + + void LabelModelItem::set_w_honor_aspect( double w ) + { + double aspect_ratio = m_h / m_w; + double h = w * aspect_ratio; + + if ( ( m_w != w ) || ( m_h != h ) ) + { + m_w = w; + m_h = h; + + changed(); + } + } + + + void LabelModelItem::set_h_honor_aspect( double h ) + { + double aspect_ratio = m_h / m_w; + double w = h / aspect_ratio; + + if ( ( m_w != w ) || ( m_h != h ) ) + { + m_w = w; + m_h = h; + + changed(); + } + } + + + LabelRegion LabelModelItem::get_extent() + { + QPointF a1( - line_width()/2, - line_width()/2 ); + QPointF a2( m_w + line_width()/2, - line_width()/2 ); + QPointF a3( m_w + line_width()/2, m_h + line_width()/2 ); + QPointF a4( - line_width()/2, m_h + line_width()/2 ); + + a1 = m_matrix.map( a1 ); + a2 = m_matrix.map( a2 ); + a3 = m_matrix.map( a3 ); + a4 = m_matrix.map( a4 ); + + LabelRegion region; + region.x1( std::min( a1.x(), std::min( a2.x(), std::min( a3.x(), a4.x() ) ) ) + m_x0 ); + region.y1( std::min( a1.y(), std::min( a2.y(), std::min( a3.y(), a4.y() ) ) ) + m_y0 ); + region.x2( std::max( a1.x(), std::max( a2.x(), std::max( a3.x(), a4.x() ) ) ) + m_x0 ); + region.y2( std::max( a1.y(), std::max( a2.y(), std::max( a3.y(), a4.y() ) ) ) + m_y0 ); + + return region; + } + + + void LabelModelItem::rotate( double theta_degs ) + { + if ( theta_degs != 0 ) + { + m_matrix = m_matrix.rotate( theta_degs ); + changed(); + } + } + + + void LabelModelItem::flip_horiz() + { + m_matrix = m_matrix.scale( -1, 1 ); + changed(); + } + + + void LabelModelItem::flip_vert() + { + m_matrix = m_matrix.scale( 1, -1 ); + changed(); + } + + + void LabelModelItem::draw( QPainter &qp, bool in_editor, const MergeRecord &record ) + { + qp.save(); + qp.translate( m_x0, m_y0 ); + + if ( m_shadow_state ) + { + qp.save(); + qp.translate( m_shadow_x, m_shadow_y ); + qp.setTransform( m_matrix, true ); + draw_shadow( qp, in_editor, record ); + qp.restore(); + } + + qp.setTransform( m_matrix, true ); + draw_object( qp, in_editor, record ); + + qp.restore(); + } + + + void LabelModelItem::draw_selection_layer( QPainter &qp ) + { + if ( m_selected_flag ) + { + qp.save(); + qp.translate( m_x0, m_y0 ); + qp.setTransform( m_matrix, true ); + +/* TODO: + if ( outline != null ) + { + outline.draw( qp ); + } + + foreach( Handle handle in handles ) + { + handle.draw( qp ); + } +*/ + + qp.restore(); + } + } + + + bool LabelModelItem::is_located_at( QPainter &qp, double x_pixels, double y_pixels ) + { + bool ret_val = false; + + qp.save(); + qp.translate( m_x0, m_y0 ); + qp.setTransform( m_matrix, true ); + +/* TODO: + double x = x_pixels; + double y = y_pixels; + qp.device_to_user( ref x, ref y ); + + bool ret_val = is_object_located_at( qp, x, y ); + + if ( (outline != null) && is_selected() ) + { + if ( outline.in_stroke( qp, x, y ) ) + { + ret_val = true; + } + } +*/ + + qp.restore(); + + return ret_val; + } + + +/* TODO: + Handle *LabelModelItem::handle_at( QPainter &qp, double x_pixels, double y_pixels ) + { + Handle *ret_val = null; + + qp.save(); + qp.translate( x0, y0 ); + qp.transform( matrix ); + + double x = x_pixels; + double y = y_pixels; + qp.device_to_user( ref x, ref y ); + + foreach( Handle handle in handles ) + { + handle.cairo_path( qp ); + + if ( qp.in_fill( x, y ) ) + { + ret_val = handle; + break; + } + } + + qp.restore(); + + return ret_val; + } +*/ + +} + + +/////////////////////////////////////////////////////////////////////////////// +#if 0 + + protected List handles; + protected Outline? outline; + + /** + * Parent label + */ + public weak Label parent { get; set; } + +#endif diff --git a/app/LabelModelItem.h b/app/LabelModelItem.h new file mode 100644 index 0000000..3df085e --- /dev/null +++ b/app/LabelModelItem.h @@ -0,0 +1,483 @@ +/* LabelModelItem.h + * + * Copyright (C) 2013 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + +#ifndef qtlabels_LabelModelItem_h +#define qtlabels_LabelModelItem_h + +#include +#include +#include +#include + +#include "ColorNode.h" +#include "TextNode.h" +#include "BarcodeStyle.h" +#include "LabelRegion.h" +#include "MergeRecord.h" + + +namespace qtLabels +{ + + class LabelModelItem : public QObject + { + Q_OBJECT + + /////////////////////////////////////////////////////////////// + // Lifecycle Methods + /////////////////////////////////////////////////////////////// + public: + LabelModelItem(); + virtual ~LabelModelItem() {} + + + /////////////////////////////////////////////////////////////// + // Signals + /////////////////////////////////////////////////////////////// + signals: + void moved(); + void changed(); + + + /////////////////////////////////////////////////////////////// + // Common Properties + /////////////////////////////////////////////////////////////// + public: + /* + * Selected Property. + */ + Q_PROPERTY( bool selected READ is_selected WRITE select RESET unselect ) + + bool is_selected( void ) { return m_selected_flag; } + void select( bool value = true ) { m_selected_flag = value; } + void unselect( void ) { m_selected_flag = false; } + + + /* + * x0 Property ( x coordinate of origin ) + */ + Q_PROPERTY( double x0 READ x0 WRITE x0 ); + + double x0( void ) { return m_x0; } + void x0( double value ) + { + if ( m_x0 != value ) { m_x0 = value; emit moved(); } + } + + + /* + * y0 Property ( y coordinate of origin ) + */ + Q_PROPERTY( double y0 READ y0 WRITE y0 ); + + double y0( void ) { return m_y0; } + void y0( double value ) + { + if ( m_y0 != value ) { m_y0 = value; emit moved(); } + } + + + /* + * w Property ( width of bounding box ) + */ + Q_PROPERTY( double w READ w WRITE w ); + + double w( void ) { return m_w; } + void w( double value ) + { + if ( m_w != value ) { m_w = value; emit moved(); } + } + + + /* + * h Property ( height of bounding box ) + */ + Q_PROPERTY( double h READ h WRITE h ); + + double h( void ) { return m_h; } + void h( double value ) + { + if ( m_h != value ) { m_h = value; emit moved(); } + } + + + /* + * Transformation Matrix Property + */ + Q_PROPERTY( QTransform matrix READ matrix WRITE matrix ); + + QTransform matrix( void ) { return m_matrix; } + void matrix( const QTransform &value ) + { + if ( m_matrix != value ) { m_matrix = value; emit changed(); } + } + + + /* + * Shadow State Property + */ + Q_PROPERTY( bool shadow READ shadow WRITE shadow ); + + bool shadow( void ) { return m_shadow_state; } + void shadow( bool value ) + { + if ( m_shadow_state != value ) { m_shadow_state = value; emit changed(); } + } + + + /* + * Shadow x Offset Property + */ + Q_PROPERTY( double shadow_x READ shadow_x WRITE shadow_x ); + + double shadow_x( void ) { return m_shadow_x; } + void shadow_x( double value ) + { + if ( m_shadow_x != value ) { m_shadow_x = value; emit changed(); } + } + + + /* + * Shadow y Offset Property + */ + Q_PROPERTY( double shadow_y READ shadow_y WRITE shadow_y ); + + double shadow_y( void ) { return m_shadow_y; } + void shadow_y( double value ) + { + if ( m_shadow_y != value ) { m_shadow_y = value; emit changed(); } + } + + + /* + * Shadow opacity Property + */ + Q_PROPERTY( double shadow_opacity READ shadow_opacity WRITE shadow_opacity ); + + double shadow_opacity( void ) { return m_shadow_opacity; } + void shadow_opacity( double value ) + { + if ( m_shadow_opacity != value ) { m_shadow_opacity = value; emit changed(); } + } + + + /* + * Shadow Color Property + */ + Q_PROPERTY( ColorNode shadow_color_node READ shadow_color_node WRITE shadow_color_node ); + + ColorNode shadow_color_node( void ) { return m_shadow_color_node; } + void shadow_color_node( const ColorNode &value ) + { + if ( m_shadow_color_node != value ) { m_shadow_color_node = value; emit changed(); } + } + + + /////////////////////////////////////////////////////////////// + // Text Properties Virtual Interface + /////////////////////////////////////////////////////////////// + public: + /* + * Virtual Text Property: font_family + */ + Q_PROPERTY( QString font_family READ font_family WRITE font_family ); + + virtual QString font_family( void ) { return ""; } + virtual void font_family( const QString &value ) { } + + + /* + * Virtual Text Property: font_size + */ + Q_PROPERTY( double font_size READ font_size WRITE font_size ); + + virtual double font_size( void ) { return 0; } + virtual void font_size( double value ) { } + + + /* + * Virtual Text Property: font_weight + */ + Q_PROPERTY( QFont::Weight font_weight READ font_weight WRITE font_weight ); + + virtual QFont::Weight font_weight( void ) { return QFont::Normal; } + virtual void font_weight( QFont::Weight value ) { } + + + /* + * Virtual Text Property: font_italic_flag + */ + Q_PROPERTY( bool font_italic_flag READ font_italic_flag WRITE font_italic_flag ); + + virtual bool font_italic_flag( void ) { return false; } + virtual void font_italic_flag( bool value ) { } + + + /* + * Virtual Text Property: font_underline_flag + */ + Q_PROPERTY( bool font_underline_flag READ font_underline_flag WRITE font_underline_flag ); + + virtual bool font_underline_flag( void ) { return false; } + virtual void font_underline_flag( bool value ) { } + + + /* + * Virtual Text Property: font_color_node + */ + Q_PROPERTY( ColorNode font_color_node READ font_color_node WRITE font_color_node ); + + virtual ColorNode font_color_node( void ) { return ColorNode( Color::none() ); } + virtual void font_color_node( const ColorNode &value ) { } + + + /* + * Virtual Text Property: text_halign + */ + Q_PROPERTY( Qt::Alignment text_halign READ text_halign WRITE text_halign ); + + virtual Qt::Alignment text_halign( void ) { return Qt::AlignLeft; } + virtual void text_halign( Qt::Alignment value ) { } + + + /* + * Virtual Text Property: text_valign + */ + Q_PROPERTY( Qt::Alignment text_valign READ text_valign WRITE text_valign ); + + virtual Qt::Alignment text_valign( void ) { return Qt::AlignTop; } + virtual void text_valign( Qt::Alignment value ) { } + + + /* + * Virtual Text Property: text_line_spacing + */ + Q_PROPERTY( double text_line_spacing READ text_line_spacing WRITE text_line_spacing ); + + virtual double text_line_spacing( void ) { return 0; } + virtual void text_line_spacing( double value ) { } + + + /////////////////////////////////////////////////////////////// + // Image Properties Virtual Interface + /////////////////////////////////////////////////////////////// + public: + /* + * Virtual Image Property: filename_node + */ + Q_PROPERTY( TextNode filename_node READ filename_node WRITE filename_node ); + + virtual TextNode filename_node( void ) { return TextNode(); } + virtual void filename_node( const TextNode &value ) { } + + + /////////////////////////////////////////////////////////////// + // Shape Properties Virtual Interface + /////////////////////////////////////////////////////////////// + public: + /* + * Virtual Shape Property: line_width + */ + Q_PROPERTY( double line_width READ line_width WRITE line_width ); + + virtual double line_width( void ) { return 0; } + virtual void line_width( double value ) { } + + + /* + * Virtual Shape Property: line_color_node + */ + Q_PROPERTY( ColorNode line_color_node READ line_color_node WRITE line_color_node ); + + virtual ColorNode line_color_node( void ) { return ColorNode( Color::none() ); } + virtual void line_color_node( const ColorNode &value ) { } + + + /* + * Virtual Shape Property: fill_color_node + */ + Q_PROPERTY( ColorNode fill_color_node READ fill_color_node WRITE fill_color_node ); + + virtual ColorNode fill_color_node( void ) { return ColorNode( Color::none() ); } + virtual void fill_color_node( const ColorNode &value ) { } + + + /////////////////////////////////////////////////////////////// + // Barcode Properties Virtual Interface + /////////////////////////////////////////////////////////////// + public: + /* + * Virtual Barcode Property: bc_data_node + */ + Q_PROPERTY( TextNode bc_data_node READ bc_data_node WRITE bc_data_node ); + + virtual TextNode bc_data_node( void ) { return TextNode(); } + virtual void bc_data_node( const TextNode &value ) { } + + + /* + * Virtual Barcode Property: bc_text_flag + */ + Q_PROPERTY( bool bc_text_flag READ bc_text_flag WRITE bc_text_flag ); + + virtual bool bc_text_flag( void ) { return false; } + virtual void bc_text_flag( bool value ) { } + + + /* + * Virtual Barcode Property: bc_checksum_flag + */ + Q_PROPERTY( bool bc_checksum_flag READ bc_checksum_flag WRITE bc_checksum_flag ); + + virtual bool bc_checksum_flag( void ) { return false; } + virtual void bc_checksum_flag( bool value ) { } + + + /* + * Virtual Barcode Property: bc_color_node + */ + Q_PROPERTY( ColorNode bc_color_node READ bc_color_node WRITE bc_color_node ); + + virtual ColorNode bc_color_node( void ) { return ColorNode( Color::none() ); } + virtual void bc_color_node( const ColorNode &value ) { } + + + /* + * Virtual Barcode Property: bc_style + */ + Q_PROPERTY( BarcodeStyle bc_style READ bc_style WRITE bc_style ); + + virtual BarcodeStyle bc_style( void ) { return BarcodeStyle(); } + virtual void bc_style( const BarcodeStyle &value ) { } + + + /* + * Virtual Barcode Property: bc_format_digits + */ + Q_PROPERTY( int bc_format_digits READ bc_format_digits WRITE bc_format_digits ); + + virtual int bc_format_digits( void ) { return false; } + virtual void bc_format_digits( int value ) { } + + + /////////////////////////////////////////////////////////////// + // Capabilities (Overridden by concrete classes.) + /////////////////////////////////////////////////////////////// + public: + virtual bool can_text() { return false; } + + virtual bool can_fill() { return false; } + + virtual bool can_line_color() { return false; } + + virtual bool can_line_width() { return false; } + + + /////////////////////////////////////////////////////////////// + // Position and Size methods + /////////////////////////////////////////////////////////////// + public: + void set_position( double x0, + double y0 ); + + void set_position_relative( double dx, + double dy ); + + void set_size( double w, + double h ); + + void set_size_honor_aspect( double w, + double h ); + + void set_w_honor_aspect( double w ); + + void set_h_honor_aspect( double h ); + + LabelRegion get_extent(); + + void rotate( double theta_degs ); + + void flip_horiz(); + + void flip_vert(); + + + /////////////////////////////////////////////////////////////// + // Drawing Methods + /////////////////////////////////////////////////////////////// + public: + void draw( QPainter &qp, bool in_editor, const MergeRecord &record ); + + void draw_selection_layer( QPainter &qp ); + + protected: + virtual void draw_object( QPainter &qp, bool in_editor, MergeRecord record ) = 0; + + virtual void draw_shadow( QPainter &qp, bool in_editor, MergeRecord record ) = 0; + + + /////////////////////////////////////////////////////////////// + // Text location Methods + /////////////////////////////////////////////////////////////// + public: + bool is_located_at( QPainter &qp, double x_pixels, double y_pixels ); + + //Handle* handle_at( QPainter &qp, double x_pixels, double y_pixels ); + + protected: + virtual bool is_object_located_at( QPainter &qp, double x, double y ) = 0; + + + /////////////////////////////////////////////////////////////// + // Private Members + /////////////////////////////////////////////////////////////// + private: + bool m_selected_flag; + + double m_x0; + double m_y0; + double m_w; + double m_h; + + QTransform m_matrix; + + bool m_shadow_state; + double m_shadow_x; + double m_shadow_y; + double m_shadow_opacity; + ColorNode m_shadow_color_node; + + }; + +} + +#endif // qtlabels_LabelModelItem_h + +/////////////////////////////////////////////////////////////////////////////// +#if 0 + protected List handles; + protected Outline? outline; + + /** + * Parent label + */ + public weak Label parent { get; set; } + +#endif diff --git a/app/LabelRegion.cpp b/app/LabelRegion.cpp new file mode 100644 index 0000000..01938b0 --- /dev/null +++ b/app/LabelRegion.cpp @@ -0,0 +1,21 @@ +/* LabelRegion.cpp + * + * Copyright (C) 2013 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + +#include "LabelRegion.h" diff --git a/app/LabelRegion.h b/app/LabelRegion.h new file mode 100644 index 0000000..27b8363 --- /dev/null +++ b/app/LabelRegion.h @@ -0,0 +1,53 @@ +/* LabelRegion.h + * + * Copyright (C) 2013 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + +#ifndef qtlabels_LabelRegion_h +#define qtlabels_LabelRegion_h + + +namespace qtLabels +{ + + struct LabelRegion + { + public: + inline double x1( void ) { return m_x1; } + inline void x1( double value ) { m_x1 = value; } + + inline double y1( void ) { return m_y1; } + inline void y1( double value ) { m_y1 = value; } + + inline double x2( void ) { return m_x2; } + inline void x2( double value ) { m_x2 = value; } + + inline double y2( void ) { return m_y2; } + inline void y2( double value ) { m_y2 = value; } + + + private: + double m_x1; + double m_y1; + double m_x2; + double m_y2; + }; + +} + +#endif // qtlabels_LabelRegion_h diff --git a/app/MergeField.cpp b/app/MergeField.cpp new file mode 100644 index 0000000..a29e26e --- /dev/null +++ b/app/MergeField.cpp @@ -0,0 +1,21 @@ +/* MergeField.cpp + * + * Copyright (C) 2013 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + +#include "MergeField.h" diff --git a/app/MergeField.h b/app/MergeField.h new file mode 100644 index 0000000..8072dba --- /dev/null +++ b/app/MergeField.h @@ -0,0 +1,47 @@ +/* MergeField.h + * + * Copyright (C) 2013 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + +#ifndef qtlabels_MergeField_h +#define qtlabels_MergeField_h + +#include + + +namespace qtLabels +{ + + struct MergeField + { + public: + inline QString key( void ) { return m_key; } + inline void key( const QString &value ) { m_key = value; } + + inline QString value( void ) { return m_value; } + inline void value( const QString &value ) { m_value = value; } + + + private: + QString m_key; + QString m_value; + }; + +} + +#endif // qtlabels_MergeField_h diff --git a/app/MergeRecord.cpp b/app/MergeRecord.cpp new file mode 100644 index 0000000..8cb053b --- /dev/null +++ b/app/MergeRecord.cpp @@ -0,0 +1,22 @@ +/* MergeRecord.cpp + * + * Copyright (C) 2013 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + +#include "MergeRecord.h" + diff --git a/app/MergeRecord.h b/app/MergeRecord.h new file mode 100644 index 0000000..7c84d5f --- /dev/null +++ b/app/MergeRecord.h @@ -0,0 +1,58 @@ +/* MergeRecord.h + * + * Copyright (C) 2013 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + +#ifndef qtlabels_MergeRecord_h +#define qtlabels_MergeRecord_h + +#include +#include + +#include "MergeField.h" + +using namespace std; + + +namespace qtLabels +{ + + struct MergeRecord + { + public: + MergeRecord() : m_selected( false ) + { + } + + + public: + inline bool selected( void ) { return m_selected; } + inline void selected( bool value ) { m_selected = value; } + + inline list field_list( void ) { return m_field_list; } + inline void field_list( list &value ) { m_field_list = value; } + + + private: + bool m_selected; + list m_field_list; + }; + +} + +#endif // qtlabels_MergeRecord_h diff --git a/app/TextNode.cpp b/app/TextNode.cpp new file mode 100644 index 0000000..6bd18eb --- /dev/null +++ b/app/TextNode.cpp @@ -0,0 +1,231 @@ +/* TextNode.cpp + * + * Copyright (C) 2013 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + +#include "TextNode.h" + + +namespace { + typedef enum { + START, + LITERAL, + LITERAL_DOLLAR, + START_DOLLAR, + FIELD, + DONE + } State; +} + + +namespace qtLabels +{ + + TextNode::TextNode( const QString &text, int i_start, int &i_next ) + { + State state = START; + QString literal_text; + QString field_name; + bool field_flag = false; + + int i = i_start; + + while ( state != DONE ) + { + QChar c = text[i]; + + switch (state) { + + case START: + switch (c.unicode()) { + case '$': + /* May be start of a field node. */ + i++; + state = START_DOLLAR; + break; + case '\n': + state = DONE; + break; + case 0: + state = DONE; + break; + default: + /* Start a literal text node. */ + literal_text.append( c ); + i++; + state = LITERAL; + break; + } + break; + + case LITERAL: + switch (c.unicode()) { + case '$': + /* May be the beginning of a field node. */ + i++; + state = LITERAL_DOLLAR; + break; + case '\n': + state = DONE; + break; + case 0: + state = DONE; + break; + default: + literal_text.append( c ); + i++; + state = LITERAL; + break; + } + break; + + case LITERAL_DOLLAR: + switch (c.unicode()) { + case '{': + /* "${" indicates the start of a new field node, gather for literal too. */ + literal_text.append( '$' ); + i++; + state = DONE; + break; + case '\n': + /* Append "$" to literal text, don't gather newline. */ + literal_text.append( '$' ); + i++; + state = DONE; + break; + case 0: + /* Append "$" to literal text, don't gather null. */ + literal_text.append( '$' ); + i++; + state = DONE; + break; + default: + /* Append "$" to literal text, gather this character too. */ + literal_text.append( '$' ); + literal_text.append( c ); + i+=2; + state = LITERAL; + break; + } + break; + + case START_DOLLAR: + switch (c.unicode()) { + case '{': + /* This is probably the begining of a field node, gather for literal too. */ + literal_text.append( c ); + i++; + state = FIELD; + break; + case '\n': + state = DONE; + break; + case 0: + state = DONE; + break; + default: + /* The "$" was literal. */ + literal_text.append( c ); + i++; + state = LITERAL; + break; + } + break; + + case FIELD: + switch (c.unicode()) { + case '}': + /* We now finally know that this node is really field node. */ + field_flag = true; + i++; + state = DONE; + break; + case '\n': + state = DONE; + break; + case 0: + state = DONE; + break; + default: + /* Gather for field name and literal, just in case. */ + field_name.append( c ); + literal_text.append( c ); + i++; + state = FIELD; + break; + } + break; + + } + + } + + m_field_flag = field_flag; + m_data = field_flag ? field_name : literal_text; + + i_next = i; + } + + +#if TODO + public string expand( MergeRecord? record ) + { + if ( field_flag ) + { + + if ( record == null ) + { + return "${%s}".printf( data ); + } + else + { + string? text = record.eval_key( data ); + if ( text != null ) + { + return text; + } + else + { + return ""; + } + } + + } + else + { + return data; + } + } + + + public bool is_empty_field( MergeRecord? record ) + { + if ( (record !=null) && field_flag ) + { + string? text = record.eval_key( data ); + return ( (text == null) || (text == "") ); + } + else + { + return false; + } + } +#endif + +} + diff --git a/app/TextNode.h b/app/TextNode.h new file mode 100644 index 0000000..9f24f3b --- /dev/null +++ b/app/TextNode.h @@ -0,0 +1,82 @@ +/* TextNode.h + * + * Copyright (C) 2013 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + +#ifndef qtlabels_TextNode_h +#define qtlabels_TextNode_h + +#include + + +namespace qtLabels +{ + + class TextNode + { + + public: + TextNode() + : m_field_flag(false), m_data("") + { + } + + + TextNode( bool field_flag, const QString &data ) + : m_field_flag(field_flag), m_data(data) + { + } + + virtual ~TextNode() {} + + + TextNode( const QString &text, int i_start, int &i_next ); + + + bool operator==( const TextNode &tn ) + { + return ( (m_field_flag == tn.m_field_flag) && + (m_data == tn.m_data) ); + } + + + bool operator!=( const TextNode &tn ) + { + return ( (m_field_flag != tn.m_field_flag) || + (m_data != tn.m_data) ); + } + + +#if TODO + string expand( MergeRecord? record ); + bool is_empty_field( MergeRecord? record ); +#endif + + bool field_flag( void ) { return m_field_flag; } + QString data( void ) { return m_data; } + + private: + + bool m_field_flag; + QString m_data; + + }; + +} + +#endif // qtlabels_TextNode_h diff --git a/app/qtlabels_main.cpp b/app/qtlabels_main.cpp new file mode 100644 index 0000000..b0fa244 --- /dev/null +++ b/app/qtlabels_main.cpp @@ -0,0 +1,33 @@ +/* qtlabels_main.cpp + * + * Copyright (C) 2011 Jim Evins + * + * This file is part of qtLabels. + * + * qtLabels is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * qtLabels is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with qtLabels. If not, see . + */ + + +#include +#include + +int main( int argc, char **argv ) +{ + QApplication app( argc, argv ); + + QLabel label( "Coming Soon..." ); + label.show(); + + return app.exec(); +}